From 5c1fdcd5e51a4d10f98dd15bdec582419e3e49f4 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Thu, 5 Jul 2018 11:21:23 -0400 Subject: [PATCH 01/20] Initial swagger doc for REST API --- backend/build.gradle | 3 + .../admin/ui/configuration/SwaggerConfig.java | 23 + ...DynamicHttpMetadataProviderController.java | 111 -- ...eBackedHttpMetadataProviderController.java | 110 -- ...ocalDynamicMetadataProviderController.java | 110 -- docs/swagger.yaml | 1159 +++++++++++++++++ 6 files changed, 1185 insertions(+), 331 deletions(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SwaggerConfig.java delete mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java delete mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java delete mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java create mode 100644 docs/swagger.yaml diff --git a/backend/build.gradle b/backend/build.gradle index 47dab440a..45a500c86 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -96,6 +96,9 @@ dependencies { runtimeOnly "org.postgresql:postgresql" runtimeOnly 'org.mariadb.jdbc:mariadb-java-client:2.2.0' + //Swagger + compile 'io.springfox:springfox-swagger2:2.9.2' + testCompile "org.springframework.boot:spring-boot-starter-test" testCompile "org.spockframework:spock-core:1.1-groovy-2.4" testCompile "org.spockframework:spock-spring:1.1-groovy-2.4" diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SwaggerConfig.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SwaggerConfig.java new file mode 100644 index 000000000..1f48b6e2f --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SwaggerConfig.java @@ -0,0 +1,23 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.regex("/api/.*")) + .build(); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java deleted file mode 100644 index 866f54231..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java +++ /dev/null @@ -1,111 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller; - -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver; -import edu.internet2.tier.shibboleth.admin.ui.repository.DynamicHttpMetadataResolverRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; - -import java.net.URI; - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@RestController -@RequestMapping("/api/MetadataProvider/DynamicHttp") -public class DynamicHttpMetadataProviderController { - private static final Logger logger = LoggerFactory.getLogger(DynamicHttpMetadataProviderController.class); - - @Autowired - DynamicHttpMetadataResolverRepository repository; - - @DeleteMapping("/{resourceId}") - public ResponseEntity deleteByResourceId(@PathVariable String resourceId) { - if (repository.deleteByResourceId(resourceId)) { - return ResponseEntity.accepted().build(); - } else { - return ResponseEntity.notFound().build(); - } - } - - @GetMapping("/name/{metadataProviderName}") - @Transactional(readOnly = true) - public ResponseEntity getOneByName(@PathVariable String metadataProviderName) { - DynamicHttpMetadataResolver resolver = repository.findByName(metadataProviderName); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @GetMapping("/{resourceId}") - @Transactional(readOnly = true) - public ResponseEntity getOneByResourceId(@PathVariable String resourceId) { - DynamicHttpMetadataResolver resolver = repository.findByResourceId(resourceId); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @PostMapping - public ResponseEntity create(@RequestBody DynamicHttpMetadataResolver resolver) { - if (repository.findByName(resolver.getName()) != null) { - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - DynamicHttpMetadataResolver persistedResolver = repository.save(resolver); - persistedResolver.setVersion(persistedResolver.hashCode()); - - return ResponseEntity - .created(getResourceUriFor(persistedResolver)) - .body(persistedResolver); - } - - @PutMapping - public ResponseEntity update(@RequestBody DynamicHttpMetadataResolver resolver) { - DynamicHttpMetadataResolver existingResolver = repository.findByResourceId(resolver.getResourceId()); - - if (existingResolver == null) { - return ResponseEntity.notFound().build(); - } - - if (existingResolver.hashCode() != resolver.getVersion()) { - logger.info("Comparing: " + existingResolver.hashCode() + " with " + resolver.getVersion()); - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - resolver.setAudId(existingResolver.getAudId()); - //TODO: Do we need to set anything else? dates? - - DynamicHttpMetadataResolver updatedResolver = repository.save(resolver); - updatedResolver.setVersion(updatedResolver.hashCode()); - - return ResponseEntity.ok(updatedResolver); - } - - private static URI getResourceUriFor(DynamicHttpMetadataResolver resolver) { - return ServletUriComponentsBuilder - .fromCurrentServletMapping().path("/api/MetadataProvider/DynamicHttp/") - .pathSegment(resolver.getResourceId()) - .build() - .toUri(); - } - -} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java deleted file mode 100644 index 5b8000482..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java +++ /dev/null @@ -1,110 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller; - -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver; -import edu.internet2.tier.shibboleth.admin.ui.repository.FileBackedHttpMetadataResolverRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; - -import java.net.URI; - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@RestController -@RequestMapping("/api/MetadataProvider/FileBackedHttp") -public class FileBackedHttpMetadataProviderController { - private static final Logger logger = LoggerFactory.getLogger(FileBackedHttpMetadataProviderController.class); - - @Autowired - FileBackedHttpMetadataResolverRepository repository; - - @DeleteMapping("/{resourceId}") - public ResponseEntity deleteByResourceId(@PathVariable String resourceId) { - if (repository.deleteByResourceId(resourceId)) { - return ResponseEntity.accepted().build(); - } else { - return ResponseEntity.notFound().build(); - } - } - - @GetMapping("/name/{metadataProviderName}") - @Transactional(readOnly = true) - public ResponseEntity getOneByName(@PathVariable String metadataProviderName) { - FileBackedHttpMetadataResolver resolver = repository.findByName(metadataProviderName); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @GetMapping("/{resourceId}") - @Transactional(readOnly = true) - public ResponseEntity getOneByResourceId(@PathVariable String resourceId) { - FileBackedHttpMetadataResolver resolver = repository.findByResourceId(resourceId); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @PostMapping - public ResponseEntity create(@RequestBody FileBackedHttpMetadataResolver resolver) { - if (repository.findByName(resolver.getName()) != null) { - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - FileBackedHttpMetadataResolver persistedResolver = repository.save(resolver); - persistedResolver.setVersion(persistedResolver.hashCode()); - - return ResponseEntity - .created(getResourceUriFor(persistedResolver)) - .body(persistedResolver); - } - - @PutMapping - public ResponseEntity update(@RequestBody FileBackedHttpMetadataResolver resolver) { - FileBackedHttpMetadataResolver existingResolver = repository.findByResourceId(resolver.getResourceId()); - - if (existingResolver == null) { - return ResponseEntity.notFound().build(); - } - - if (existingResolver.hashCode() != resolver.getVersion()) { - logger.info("Comparing: " + existingResolver.hashCode() + " with " + resolver.getVersion()); - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - resolver.setAudId(existingResolver.getAudId()); - //TODO: Do we need to set anything else? dates? - - FileBackedHttpMetadataResolver updatedResolver = repository.save(resolver); - updatedResolver.setVersion(updatedResolver.hashCode()); - - return ResponseEntity.ok(updatedResolver); - } - - private static URI getResourceUriFor(FileBackedHttpMetadataResolver resolver) { - return ServletUriComponentsBuilder - .fromCurrentServletMapping().path("/api/MetadataProvider/FileBackedHttp/") - .pathSegment(resolver.getResourceId()) - .build() - .toUri(); - } -} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java deleted file mode 100644 index 2d3a104ee..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java +++ /dev/null @@ -1,110 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller; - -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver; -import edu.internet2.tier.shibboleth.admin.ui.repository.LocalDynamicMetadataResolverRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; - -import java.net.URI; - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@RestController -@RequestMapping("/api/MetadataProvider/LocalDynamic") -public class LocalDynamicMetadataProviderController { - private static final Logger logger = LoggerFactory.getLogger(LocalDynamicMetadataProviderController.class); - - @Autowired - LocalDynamicMetadataResolverRepository repository; - - @DeleteMapping("/{resourceId}") - public ResponseEntity deleteByResourceId(@PathVariable String resourceId) { - if (repository.deleteByResourceId(resourceId)) { - return ResponseEntity.accepted().build(); - } else { - return ResponseEntity.notFound().build(); - } - } - - @GetMapping("/name/{metadataProviderName}") - @Transactional(readOnly = true) - public ResponseEntity getOneByName(@PathVariable String metadataProviderName) { - LocalDynamicMetadataResolver resolver = repository.findByName(metadataProviderName); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @GetMapping("/{resourceId}") - @Transactional(readOnly = true) - public ResponseEntity getOneByResourceId(@PathVariable String resourceId) { - LocalDynamicMetadataResolver resolver = repository.findByResourceId(resourceId); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @PostMapping - public ResponseEntity create(@RequestBody LocalDynamicMetadataResolver resolver) { - if (repository.findByName(resolver.getName()) != null) { - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - LocalDynamicMetadataResolver persistedResolver = repository.save(resolver); - persistedResolver.setVersion(persistedResolver.hashCode()); - - return ResponseEntity - .created(getResourceUriFor(persistedResolver)) - .body(persistedResolver); - } - - @PutMapping - public ResponseEntity update(@RequestBody LocalDynamicMetadataResolver resolver) { - LocalDynamicMetadataResolver existingResolver = repository.findByResourceId(resolver.getResourceId()); - - if (existingResolver == null) { - return ResponseEntity.notFound().build(); - } - - if (existingResolver.hashCode() != resolver.getVersion()) { - logger.info("Comparing: " + existingResolver.hashCode() + " with " + resolver.getVersion()); - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - resolver.setAudId(existingResolver.getAudId()); - //TODO: Do we need to set anything else? dates? - - LocalDynamicMetadataResolver updatedResolver = repository.save(resolver); - updatedResolver.setVersion(updatedResolver.hashCode()); - - return ResponseEntity.ok(updatedResolver); - } - - private static URI getResourceUriFor(LocalDynamicMetadataResolver resolver) { - return ServletUriComponentsBuilder - .fromCurrentServletMapping().path("/api/MetadataProvider/LocalDynamic/") - .pathSegment(resolver.getResourceId()) - .build() - .toUri(); - } -} diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 000000000..142b6fe0c --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,1159 @@ +swagger: '2.0' +info: + description: Api Documentation + version: '1.0' + title: Api Documentation + termsOfService: 'urn:tos' + contact: {} + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0' +host: 'localhost:8080' +basePath: / +tags: + - name: entities-controller + description: Entities Controller + - name: entity-descriptor-controller + description: Entity Descriptor Controller + - name: entity-ids-search-controller + description: Entity Ids Search Controller + - name: metadata-filters-controller + description: Metadata Filters Controller + - name: metadata-providers-controller + description: Metadata Providers Controller + - name: metadata-resolvers-controller + description: Metadata Resolvers Controller +paths: + /api/EntityDescriptor: + post: + tags: + - entity-descriptor-controller + summary: create + operationId: createUsingPOST + consumes: + - application/json + produces: + - '*/*' + parameters: + - in: body + name: edRepresentation + description: edRepresentation + required: true + schema: + $ref: '#/definitions/EntityDescriptorRepresentation' + responses: + '200': + description: OK + schema: + type: object + '201': + description: Created + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + '/api/EntityDescriptor/{resourceId}': + get: + tags: + - entity-descriptor-controller + summary: getOne + operationId: getOneUsingGET_1 + produces: + - application/xml + parameters: + - name: resourceId + in: path + description: resourceId + required: true + type: string + responses: + '200': + description: OK + schema: + type: object + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + put: + tags: + - entity-descriptor-controller + summary: update + operationId: updateUsingPUT + consumes: + - application/json + produces: + - '*/*' + parameters: + - in: body + name: edRepresentation + description: edRepresentation + required: true + schema: + $ref: '#/definitions/EntityDescriptorRepresentation' + - name: resourceId + in: path + description: resourceId + required: true + type: string + responses: + '200': + description: OK + schema: + type: object + '201': + description: Created + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + /api/EntityDescriptors: + get: + tags: + - entity-descriptor-controller + summary: getAll + operationId: getAllUsingGET + produces: + - '*/*' + responses: + '200': + description: OK + schema: + $ref: '#/definitions/EntityDescriptorRepresentation' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + /api/EntityIds/search: + get: + tags: + - entity-ids-search-controller + summary: search + operationId: searchUsingGET + produces: + - '*/*' + parameters: + - name: limit + in: query + description: limit + required: false + type: integer + format: int32 + - name: term + in: query + description: term + required: true + type: string + responses: + '200': + description: OK + schema: + type: object + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + /api/MetadataResolvers: + get: + tags: + - metadata-resolvers-controller + summary: getAll + operationId: getAllUsingGET_2 + produces: + - '*/*' + responses: + '200': + description: OK + schema: + type: object + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + post: + tags: + - metadata-resolvers-controller + summary: create + operationId: createUsingPOST_2 + consumes: + - application/json + produces: + - '*/*' + parameters: + - in: body + name: newResolver + description: newResolver + required: true + schema: + $ref: '#/definitions/MetadataResolver' + responses: + '200': + description: OK + schema: + type: object + '201': + description: Created + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + '/api/MetadataResolvers/{metadataResolverId}/Filters': + get: + tags: + - metadata-filters-controller + summary: getAll + operationId: getAllUsingGET_1 + produces: + - '*/*' + parameters: + - name: metadataResolverId + in: path + description: metadataResolverId + required: true + type: string + responses: + '200': + description: OK + schema: + type: object + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + post: + tags: + - metadata-filters-controller + summary: create + operationId: createUsingPOST_1 + consumes: + - application/json + produces: + - '*/*' + parameters: + - in: body + name: createdFilter + description: createdFilter + required: true + schema: + $ref: '#/definitions/MetadataFilter' + - name: metadataResolverId + in: path + description: metadataResolverId + required: true + type: string + responses: + '200': + description: OK + schema: + type: object + '201': + description: Created + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + '/api/MetadataResolvers/{metadataResolverId}/Filters/{resourceId}': + get: + tags: + - metadata-filters-controller + summary: getOne + operationId: getOneUsingGET_2 + produces: + - '*/*' + parameters: + - name: metadataResolverId + in: path + description: metadataResolverId + required: true + type: string + - name: resourceId + in: path + description: resourceId + required: true + type: string + responses: + '200': + description: OK + schema: + type: object + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + put: + tags: + - metadata-filters-controller + summary: update + operationId: updateUsingPUT_1 + consumes: + - application/json + produces: + - '*/*' + parameters: + - name: metadataResolverId + in: path + description: metadataResolverId + required: true + type: string + - name: resourceId + in: path + description: resourceId + required: true + type: string + - in: body + name: updatedFilter + description: updatedFilter + required: true + schema: + $ref: '#/definitions/MetadataFilter' + responses: + '200': + description: OK + schema: + type: object + '201': + description: Created + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + '/api/MetadataResolvers/{resourceId}': + get: + tags: + - metadata-resolvers-controller + summary: getOne + operationId: getOneUsingGET_3 + produces: + - '*/*' + parameters: + - name: resourceId + in: path + description: resourceId + required: true + type: string + responses: + '200': + description: OK + schema: + type: object + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + put: + tags: + - metadata-resolvers-controller + summary: update + operationId: updateUsingPUT_2 + consumes: + - application/json + produces: + - '*/*' + parameters: + - name: resourceId + in: path + description: resourceId + required: true + type: string + - in: body + name: updatedResolver + description: updatedResolver + required: true + schema: + $ref: '#/definitions/MetadataResolver' + responses: + '200': + description: OK + schema: + type: object + '201': + description: Created + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + '/api/entities/{entityId}': + get: + tags: + - entities-controller + summary: getOne + operationId: getOneUsingGET + produces: + - '*/*' + parameters: + - name: entityId + in: path + description: entityId + required: true + type: string + responses: + '200': + description: OK + schema: + type: object + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + /api/metadataProviders: + get: + tags: + - metadata-providers-controller + summary: getXml + operationId: getXmlUsingGET + produces: + - application/xml + responses: + '200': + description: OK + schema: + type: object + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + head: + tags: + - metadata-providers-controller + summary: getXml + operationId: getXmlUsingHEAD + consumes: + - application/json + produces: + - application/xml + responses: + '200': + description: OK + schema: + type: object + '204': + description: No Content + '401': + description: Unauthorized + '403': + description: Forbidden + deprecated: false + post: + tags: + - metadata-providers-controller + summary: getXml + operationId: getXmlUsingPOST + consumes: + - application/json + produces: + - application/xml + responses: + '200': + description: OK + schema: + type: object + '201': + description: Created + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + put: + tags: + - metadata-providers-controller + summary: getXml + operationId: getXmlUsingPUT + consumes: + - application/json + produces: + - application/xml + responses: + '200': + description: OK + schema: + type: object + '201': + description: Created + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + deprecated: false + delete: + tags: + - metadata-providers-controller + summary: getXml + operationId: getXmlUsingDELETE + produces: + - application/xml + responses: + '200': + description: OK + schema: + type: object + '204': + description: No Content + '401': + description: Unauthorized + '403': + description: Forbidden + deprecated: false + options: + tags: + - metadata-providers-controller + summary: getXml + operationId: getXmlUsingOPTIONS + consumes: + - application/json + produces: + - application/xml + responses: + '200': + description: OK + schema: + type: object + '204': + description: No Content + '401': + description: Unauthorized + '403': + description: Forbidden + deprecated: false + patch: + tags: + - metadata-providers-controller + summary: getXml + operationId: getXmlUsingPATCH + consumes: + - application/json + produces: + - application/xml + responses: + '200': + description: OK + schema: + type: object + '204': + description: No Content + '401': + description: Unauthorized + '403': + description: Forbidden + deprecated: false +definitions: + AssertionConsumerServiceRepresentation: + type: object + properties: + binding: + type: string + locationUrl: + type: string + makeDefault: + type: boolean + title: AssertionConsumerServiceRepresentation + ContactRepresentation: + type: object + properties: + displayName: + type: string + emailAddress: + type: string + name: + type: string + type: + type: string + url: + type: string + title: ContactRepresentation + DynamicHttpMetadataResolver: + title: DynamicHttpMetadataResolver + allOf: + - $ref: '#/definitions/MetadataResolver' + - type: object + properties: + '@type': + type: string + audId: + type: integer + format: int64 + createdBy: + type: string + createdDate: + type: string + format: date-time + criterionPredicateRegistryRef: + type: string + dynamicMetadataResolverAttributes: + $ref: '#/definitions/DynamicMetadataResolverAttributes' + failFastInitialization: + type: boolean + httpMetadataResolverAttributes: + $ref: '#/definitions/HttpMetadataResolverAttributes' + maxConnectionsPerRoute: + type: integer + format: int32 + maxConnectionsTotal: + type: integer + format: int32 + metadataFilters: + type: array + items: + $ref: '#/definitions/MetadataFilter' + modifiedBy: + type: string + modifiedDate: + type: string + format: date-time + name: + type: string + requireValidMetadata: + type: boolean + resourceId: + type: string + satisfyAnyPredicates: + type: boolean + sortKey: + type: integer + format: int32 + supportedContentTypes: + type: array + items: + type: string + useDefaultPredicateRegistry: + type: boolean + version: + type: integer + format: int32 + title: DynamicHttpMetadataResolver + EntityAttributesFilter: + title: EntityAttributesFilter + allOf: + - $ref: '#/definitions/MetadataFilter' + - type: object + properties: + '@type': + type: string + attributeRelease: + type: array + items: + type: string + audId: + type: integer + format: int64 + createdBy: + type: string + createdDate: + type: string + format: date-time + entityAttributesFilterTarget: + $ref: '#/definitions/EntityAttributesFilterTarget' + filterEnabled: + type: boolean + modifiedBy: + type: string + modifiedDate: + type: string + format: date-time + name: + type: string + relyingPartyOverrides: + $ref: '#/definitions/RelyingPartyOverridesRepresentation' + resourceId: + type: string + version: + type: integer + format: int32 + title: EntityAttributesFilter + EntityDescriptorRepresentation: + type: object + properties: + assertionConsumerServices: + type: array + items: + $ref: '#/definitions/AssertionConsumerServiceRepresentation' + attributeRelease: + type: array + items: + type: string + contacts: + type: array + items: + $ref: '#/definitions/ContactRepresentation' + createdDate: + type: string + format: date-time + entityId: + type: string + id: + type: string + logoutEndpoints: + type: array + items: + $ref: '#/definitions/LogoutEndpointRepresentation' + mdui: + $ref: '#/definitions/MduiRepresentation' + modifiedDate: + type: string + format: date-time + organization: + $ref: '#/definitions/OrganizationRepresentation' + relyingPartyOverrides: + $ref: '#/definitions/RelyingPartyOverridesRepresentation' + securityInfo: + $ref: '#/definitions/SecurityInfoRepresentation' + serviceEnabled: + type: boolean + serviceProviderName: + type: string + serviceProviderSsoDescriptor: + $ref: '#/definitions/ServiceProviderSsoDescriptorRepresentation' + version: + type: integer + format: int32 + title: EntityDescriptorRepresentation + EntityRoleWhiteListFilter: + title: EntityRoleWhiteListFilter + allOf: + - $ref: '#/definitions/MetadataFilter' + - type: object + properties: + '@type': + type: string + audId: + type: integer + format: int64 + createdBy: + type: string + createdDate: + type: string + format: date-time + filterEnabled: + type: boolean + modifiedBy: + type: string + modifiedDate: + type: string + format: date-time + name: + type: string + removeEmptyEntitiesDescriptors: + type: boolean + removeRolelessEntityDescriptors: + type: boolean + resourceId: + type: string + retainedRoles: + type: array + items: + type: string + version: + type: integer + format: int32 + title: EntityRoleWhiteListFilter + FileBackedHttpMetadataResolver: + title: FileBackedHttpMetadataResolver + allOf: + - $ref: '#/definitions/MetadataResolver' + - type: object + properties: + '@type': + type: string + audId: + type: integer + format: int64 + backingFile: + type: string + backupFileInitNextRefreshDelay: + type: string + createdBy: + type: string + createdDate: + type: string + format: date-time + criterionPredicateRegistryRef: + type: string + failFastInitialization: + type: boolean + httpMetadataResolverAttributes: + $ref: '#/definitions/HttpMetadataResolverAttributes' + initializeFromBackupFile: + type: boolean + metadataFilters: + type: array + items: + $ref: '#/definitions/MetadataFilter' + metadataURL: + type: string + modifiedBy: + type: string + modifiedDate: + type: string + format: date-time + name: + type: string + reloadableMetadataResolverAttributes: + $ref: '#/definitions/ReloadableMetadataResolverAttributes' + requireValidMetadata: + type: boolean + resourceId: + type: string + satisfyAnyPredicates: + type: boolean + sortKey: + type: integer + format: int32 + useDefaultPredicateRegistry: + type: boolean + version: + type: integer + format: int32 + title: FileBackedHttpMetadataResolver + Iterable«EntityDescriptorRepresentation»: + type: object + title: Iterable«EntityDescriptorRepresentation» + LocalDynamicMetadataResolver: + title: LocalDynamicMetadataResolver + allOf: + - $ref: '#/definitions/MetadataResolver' + - type: object + properties: + '@type': + type: string + audId: + type: integer + format: int64 + createdBy: + type: string + createdDate: + type: string + format: date-time + criterionPredicateRegistryRef: + type: string + dynamicMetadataResolverAttributes: + $ref: '#/definitions/DynamicMetadataResolverAttributes' + failFastInitialization: + type: boolean + metadataFilters: + type: array + items: + $ref: '#/definitions/MetadataFilter' + modifiedBy: + type: string + modifiedDate: + type: string + format: date-time + name: + type: string + requireValidMetadata: + type: boolean + resourceId: + type: string + satisfyAnyPredicates: + type: boolean + sortKey: + type: integer + format: int32 + sourceDirectory: + type: string + sourceKeyGeneratorRef: + type: string + sourceManagerRef: + type: string + useDefaultPredicateRegistry: + type: boolean + version: + type: integer + format: int32 + title: LocalDynamicMetadataResolver + LogoutEndpointRepresentation: + type: object + properties: + bindingType: + type: string + url: + type: string + title: LogoutEndpointRepresentation + MduiRepresentation: + type: object + properties: + description: + type: string + displayName: + type: string + informationUrl: + type: string + logoHeight: + type: integer + format: int32 + logoUrl: + type: string + logoWidth: + type: integer + format: int32 + privacyStatementUrl: + type: string + title: MduiRepresentation + MetadataFilter: + type: object + properties: + '@type': + type: string + audId: + type: integer + format: int64 + createdBy: + type: string + createdDate: + type: string + format: date-time + filterEnabled: + type: boolean + modifiedBy: + type: string + modifiedDate: + type: string + format: date-time + name: + type: string + resourceId: + type: string + version: + type: integer + format: int32 + title: MetadataFilter + MetadataResolver: + type: object + properties: + '@type': + type: string + audId: + type: integer + format: int64 + createdBy: + type: string + createdDate: + type: string + format: date-time + criterionPredicateRegistryRef: + type: string + failFastInitialization: + type: boolean + metadataFilters: + type: array + items: + $ref: '#/definitions/MetadataFilter' + modifiedBy: + type: string + modifiedDate: + type: string + format: date-time + name: + type: string + requireValidMetadata: + type: boolean + resourceId: + type: string + satisfyAnyPredicates: + type: boolean + sortKey: + type: integer + format: int32 + useDefaultPredicateRegistry: + type: boolean + version: + type: integer + format: int32 + title: MetadataResolver + OrganizationRepresentation: + type: object + properties: + displayName: + type: string + name: + type: string + url: + type: string + title: OrganizationRepresentation + RelyingPartyOverridesRepresentation: + type: object + properties: + authenticationMethods: + type: array + items: + type: string + dontSignResponse: + type: boolean + ignoreAuthenticationMethod: + type: boolean + nameIdFormats: + type: array + items: + type: string + omitNotBefore: + type: boolean + responderId: + type: string + signAssertion: + type: boolean + turnOffEncryption: + type: boolean + useSha: + type: boolean + title: RelyingPartyOverridesRepresentation + RequiredValidUntilFilter: + title: RequiredValidUntilFilter + allOf: + - $ref: '#/definitions/MetadataFilter' + - type: object + properties: + '@type': + type: string + audId: + type: integer + format: int64 + createdBy: + type: string + createdDate: + type: string + format: date-time + filterEnabled: + type: boolean + maxValidityInterval: + type: string + modifiedBy: + type: string + modifiedDate: + type: string + format: date-time + name: + type: string + resourceId: + type: string + version: + type: integer + format: int32 + title: RequiredValidUntilFilter + SecurityInfoRepresentation: + type: object + properties: + authenticationRequestsSigned: + type: boolean + wantAssertionsSigned: + type: boolean + x509CertificateAvailable: + type: boolean + x509Certificates: + type: array + items: + $ref: '#/definitions/X509CertificateRepresentation' + title: SecurityInfoRepresentation + ServiceProviderSsoDescriptorRepresentation: + type: object + properties: + nameIdFormats: + type: array + items: + type: string + protocolSupportEnum: + type: string + title: ServiceProviderSsoDescriptorRepresentation + SignatureValidationFilter: + title: SignatureValidationFilter + allOf: + - $ref: '#/definitions/MetadataFilter' + - type: object + properties: + '@type': + type: string + audId: + type: integer + format: int64 + certificateFile: + type: string + createdBy: + type: string + createdDate: + type: string + format: date-time + defaultCriteriaRef: + type: string + dynamicTrustedNamesStrategyRef: + type: string + filterEnabled: + type: boolean + modifiedBy: + type: string + modifiedDate: + type: string + format: date-time + name: + type: string + publicKey: + type: string + requireSignedRoot: + type: boolean + resourceId: + type: string + signaturePrevalidatorRef: + type: string + trustEngineRef: + type: string + version: + type: integer + format: int32 + title: SignatureValidationFilter + X509CertificateRepresentation: + type: object + properties: + name: + type: string + type: + type: string + value: + type: string + title: X509CertificateRepresentation \ No newline at end of file From 3f2e1b894bec146170bd4ecc9195b3955c08ed30 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 9 Jul 2018 10:41:56 -0400 Subject: [PATCH 02/20] SHIBUI-619: fix tests --- ...HttpMetadataProviderControllerTests.groovy | 272 ----------------- ...HttpMetadataProviderControllerTests.groovy | 274 ----------------- ...amicMetadataProviderControllerTests.groovy | 276 ------------------ 3 files changed, 822 deletions(-) delete mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy delete mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy delete mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy deleted file mode 100644 index 5b10c9f6e..000000000 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy +++ /dev/null @@ -1,272 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller - -import com.fasterxml.jackson.databind.ObjectMapper -import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration -import edu.internet2.tier.shibboleth.admin.ui.repository.DynamicHttpMetadataResolverRepository -import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator -import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator -import edu.internet2.tier.shibboleth.admin.util.AttributeUtility -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest -import org.springframework.data.jpa.repository.config.EnableJpaRepositories -import org.springframework.test.context.ContextConfiguration -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import spock.lang.Specification - -import static org.hamcrest.CoreMatchers.containsString -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.* - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) -@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) -@EntityScan("edu.internet2.tier.shibboleth.admin.ui") -class DynamicHttpMetadataProviderControllerTests extends Specification { - RandomGenerator randomGenerator - TestObjectGenerator testObjectGenerator - ObjectMapper mapper - - def repository = Mock(DynamicHttpMetadataResolverRepository) - def controller - def mockMvc - - @Autowired - AttributeUtility attributeUtility - - def setup() { - randomGenerator = new RandomGenerator() - testObjectGenerator = new TestObjectGenerator(attributeUtility) - mapper = new ObjectMapper() - - controller = new DynamicHttpMetadataProviderController ( - repository: repository - ) - - mockMvc = MockMvcBuilders.standaloneSetup(controller).build() - } - - def "DELETE deletes the desired resolver"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> true - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/DynamicHttp/$randomResourceId")) - - then: - result.andExpect(status().isAccepted()) - } - - def "DELETE returns error when desired resolver is not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> false - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/DynamicHttp/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "POST a new resolver properly persists and returns the new persisted resolver"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - resolver.setVersion(resolver.hashCode()) - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolver.getName()) >> null - 1 * repository.save(_) >> resolver - - def expectedResolverUUID = resolver.getResourceId() - def expectedResponseHeader = 'Location' - def expectedResponseHeaderValue = "/api/MetadataProvider/DynamicHttp/$expectedResolverUUID" - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isCreated()) - .andExpect(content().json(postedJsonBody, false)) - .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) - - } - - def "POST a new resolver that has a name of a persisted resolver returns conflict"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - def resolverName = resolver.name - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolverName) >> resolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "GET by resourceId returns the desired persisted resolver"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - resolver.version = resolver.hashCode() - def resourceId = resolver.resourceId - def resolverJson = mapper.writeValueAsString(resolver) - - 1 * repository.findByResourceId(resourceId) >> resolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/DynamicHttp/$resourceId")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resource id returns not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.findByResourceId(randomResourceId) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/DynamicHttp/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "GET by resolver name returns the desired persisted resolver"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - resolver.version = resolver.hashCode() - def resolverName = resolver.name - def resolverJson = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolverName) >> resolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/DynamicHttp/name/$resolverName")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resolver name returns not found"() { - given: - def randomResolverName = randomGenerator.randomString(10) - - 1 * repository.findByName(randomResolverName) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/DynamicHttp/name/$randomResolverName")) - - then: - result.andExpect(status().isNotFound()) - } - - def "PUT allows for a successful update of an already-persisted resolver"() { - given: - def existingResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def resourceId = existingResolver.resourceId - def existingResolverJson = mapper.writeValueAsString(existingResolver) - - def updatedResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - updatedResolver.resourceId = existingResolver.resourceId - updatedResolver.version = existingResolver.version - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(existingResolver.resourceId) >> existingResolver - 1 * repository.save(_) >> updatedResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - def expectedJson = new JsonSlurper().parseText(postedJsonBody) - expectedJson << [version: updatedResolver.hashCode()] - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(JsonOutput.toJson(expectedJson), false)) - } - - def "PUT of an updated resolver with an incorrect version returns a conflict"() { - given: - def existingResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - - def updatedResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - updatedResolver.resourceId = existingResolver.resourceId - updatedResolver.version = updatedResolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(existingResolver.resourceId) >> existingResolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "PUT of a resolver that is not persisted returns not found"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByResourceId(resolver.resourceId) >> null - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isNotFound()) - } -} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy deleted file mode 100644 index 99457a335..000000000 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy +++ /dev/null @@ -1,274 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller - -import com.fasterxml.jackson.databind.ObjectMapper -import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration -import edu.internet2.tier.shibboleth.admin.ui.repository.FileBackedHttpMetadataResolverRepository -import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator -import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator -import edu.internet2.tier.shibboleth.admin.util.AttributeUtility -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest -import org.springframework.data.jpa.repository.config.EnableJpaRepositories -import org.springframework.test.context.ContextConfiguration -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import spock.lang.Specification - -import static org.hamcrest.CoreMatchers.containsString -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.* - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) -@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) -@EntityScan("edu.internet2.tier.shibboleth.admin.ui") -class FileBackedHttpMetadataProviderControllerTests extends Specification { - RandomGenerator randomGenerator - TestObjectGenerator testObjectGenerator - ObjectMapper mapper - - @Autowired - AttributeUtility attributeUtility - - def repository = Mock(FileBackedHttpMetadataResolverRepository) - def controller - def mockMvc - - def setup() { - randomGenerator = new RandomGenerator() - testObjectGenerator = new TestObjectGenerator(attributeUtility) - mapper = new ObjectMapper() - - controller = new FileBackedHttpMetadataProviderController ( - repository: repository - ) - - mockMvc = MockMvcBuilders.standaloneSetup(controller).build() - } - - def "DELETE deletes the desired resolver"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> true - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) - - then: - result.andExpect(status().isAccepted()) - } - - def "DELETE returns error when desired resolver is not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> false - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "POST a new resolver properly persists and returns the new persisted resolver"() { - given: - def resolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - resolver.version = resolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolver.getName()) >> null - 1 * repository.save(_) >> resolver - - def expectedResolverUUID = resolver.getResourceId() - def expectedResponseHeader = 'Location' - def expectedResponseHeaderValue = "/api/MetadataProvider/FileBackedHttp/$expectedResolverUUID" - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isCreated()) - .andExpect(content().json(postedJsonBody, false)) - .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) - - } - - def "POST a new resolver that has a name of a persisted resolver returns conflict"() { - given: - def existingResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - def randomResolverName = existingResolver.name - def newResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - newResolver.name = randomResolverName - def postedJsonBody = mapper.writeValueAsString(newResolver) - - 1 * repository.findByName(randomResolverName) >> existingResolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "GET by resourceId returns the desired persisted resolver"() { - given: - def existingResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def randomResourceId = existingResolver.resourceId - def resolverJson = mapper.writeValueAsString(existingResolver) - - 1 * repository.findByResourceId(randomResourceId) >> existingResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resource id returns not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.findByResourceId(randomResourceId) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "GET by resolver name returns the desired persisted resolver"() { - given: - def randomResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - randomResolver.version = randomResolver.hashCode() - def randomResolverName = randomResolver.name - def resolverJson = mapper.writeValueAsString(randomResolver) - - 1 * repository.findByName(randomResolverName) >> randomResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/FileBackedHttp/name/$randomResolverName")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resolver name returns not found"() { - given: - def randomResolverName = randomGenerator.randomString(10) - - 1 * repository.findByName(randomResolverName) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/FileBackedHttp/name/$randomResolverName")) - - then: - result.andExpect(status().isNotFound()) - } - - def "PUT allows for a successful update of an already-persisted resolver"() { - given: - def existingResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def randomResourceId = existingResolver.resourceId - def updatedResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - updatedResolver.version = existingResolver.version - updatedResolver.resourceId = existingResolver.resourceId - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - - 1 * repository.findByResourceId(randomResourceId) >> existingResolver - 1 * repository.save(_) >> updatedResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - def expectedJson = new JsonSlurper().parseText(postedJsonBody) - expectedJson << [version: updatedResolver.hashCode()] - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(JsonOutput.toJson(expectedJson), false)) - } - - def "PUT of an updated resolver with an incorrect version returns a conflict"() { - given: - def existingResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def randomResourceId = existingResolver.resourceId - def updatedResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - updatedResolver.version = updatedResolver.hashCode() - updatedResolver.resourceId = existingResolver.resourceId - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(randomResourceId) >> existingResolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "PUT of a resolver that is not persisted returns not found"() { - given: - def randomResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - randomResolver.version = randomResolver.hashCode() - def randomResourceId = randomResolver.resourceId - def postedJsonBody = mapper.writeValueAsString(randomResolver) - - 1 * repository.findByResourceId(randomResourceId) >> null - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isNotFound()) - } -} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy deleted file mode 100644 index 14c925c5c..000000000 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy +++ /dev/null @@ -1,276 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller - -import com.fasterxml.jackson.databind.ObjectMapper -import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration -import edu.internet2.tier.shibboleth.admin.ui.repository.LocalDynamicMetadataResolverRepository -import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator -import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator -import edu.internet2.tier.shibboleth.admin.util.AttributeUtility -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest -import org.springframework.data.jpa.repository.config.EnableJpaRepositories -import org.springframework.test.context.ContextConfiguration -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import spock.lang.Specification - -import static org.hamcrest.CoreMatchers.containsString -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.* - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) -@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) -@EntityScan("edu.internet2.tier.shibboleth.admin.ui") -class LocalDynamicMetadataProviderControllerTests extends Specification { - RandomGenerator randomGenerator - TestObjectGenerator testObjectGenerator - ObjectMapper mapper - - def repository = Mock(LocalDynamicMetadataResolverRepository) - def controller - def mockMvc - - @Autowired - AttributeUtility attributeUtility - - def setup() { - randomGenerator = new RandomGenerator() - testObjectGenerator = new TestObjectGenerator(attributeUtility) - mapper = new ObjectMapper() - - controller = new LocalDynamicMetadataProviderController ( - repository: repository - ) - - mockMvc = MockMvcBuilders.standaloneSetup(controller).build() - } - - def "DELETE deletes the desired resolver"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> true - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/LocalDynamic/$randomResourceId")) - - then: - result.andExpect(status().isAccepted()) - } - - def "DELETE returns error when desired resolver is not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> false - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/LocalDynamic/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "POST a new resolver properly persists and returns the new persisted resolver"() { - given: - def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - resolver.version = resolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolver.getName()) >> null - 1 * repository.save(_) >> resolver - - def expectedResolverUUID = resolver.getResourceId() - def expectedResponseHeader = 'Location' - def expectedResponseHeaderValue = "/api/MetadataProvider/LocalDynamic/$expectedResolverUUID" - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isCreated()) - .andExpect(content().json(postedJsonBody, false)) - .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) - } - - def "POST a new resolver that has a name of a persisted resolver returns conflict"() { - given: - def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - resolver.version = resolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolver.name) >> resolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "GET by resourceId returns the desired persisted resolver"() { - given: - def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - resolver.version = resolver.hashCode() - def resolverJson = mapper.writeValueAsString(resolver) - def resolverId = resolver.resourceId - - 1 * repository.findByResourceId(resolverId) >> resolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/LocalDynamic/$resolverId")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resource id returns not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.findByResourceId(randomResourceId) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/LocalDynamic/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "GET by resolver name returns the desired persisted resolver"() { - given: - def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - resolver.version = resolver.hashCode() - def resolverName = resolver.name - def resolverJson = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolverName) >> resolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/LocalDynamic/name/$resolverName")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resolver name returns not found"() { - given: - def randomResolverName = randomGenerator.randomString(10) - - 1 * repository.findByName(randomResolverName) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/LocalDynamic/name/$randomResolverName")) - - then: - result.andExpect(status().isNotFound()) - } - - def "PUT allows for a successful update of an already-persisted resolver"() { - given: - def existingResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def resourceId = existingResolver.resourceId - def updatedResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - updatedResolver.setResourceId(resourceId) - updatedResolver.setVersion(existingResolver.hashCode()) - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(resourceId) >> existingResolver - 1 * repository.save(_) >> updatedResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - def expectedJson = new JsonSlurper().parseText(postedJsonBody) - expectedJson << [version: updatedResolver.hashCode()] - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(JsonOutput.toJson(expectedJson), false)) - } - - def "PUT of an updated resolver with an incorrect version returns a conflict"() { - given: - def existingResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def resourceId = existingResolver.resourceId - - def updatedResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - updatedResolver.resourceId = resourceId - updatedResolver.version = updatedResolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(resourceId) >> existingResolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "PUT of a resolver that is not persisted returns not found"() { - given: - def existingResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def resourceId = existingResolver.resourceId - - def updatedResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - updatedResolver.resourceId = resourceId - updatedResolver.version = updatedResolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(resourceId) >> null - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isNotFound()) - } -} From 5be784a7d8b729ecbb62e9c3990924b5a7d6c83b Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 10 Jul 2018 07:30:56 -0400 Subject: [PATCH 03/20] To trigger the build --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7f5b9994..7ebba48b5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ There are currently 2 ways to run the application: 1. As an executable -1. deployed in a Java Servlet 3.0 container +2. deployed in a Java Servlet 3.0 container Note that some features require encoded slashes in the URL. In tomcat (which is embedded in the war), this can be allowed with: From d7f54a5ab0255d8ba7352a0056f267038bfca44b Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 10 Jul 2018 10:58:06 -0400 Subject: [PATCH 04/20] Revert back --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ebba48b5..a7f5b9994 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ There are currently 2 ways to run the application: 1. As an executable -2. deployed in a Java Servlet 3.0 container +1. deployed in a Java Servlet 3.0 container Note that some features require encoded slashes in the URL. In tomcat (which is embedded in the war), this can be allowed with: From 8175905c71d0249c3d777c3bde54fa664ec502b0 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 16 Jul 2018 09:17:00 -0400 Subject: [PATCH 05/20] Update swagger definition manually --- docs/swagger.yaml | 186 ++++++---------------------------------------- 1 file changed, 23 insertions(+), 163 deletions(-) diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 142b6fe0c..230146dfa 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,6 +1,6 @@ swagger: '2.0' info: - description: Api Documentation + description: Shibboleth UI REST Api Documentation version: '1.0' title: Api Documentation termsOfService: 'urn:tos' @@ -32,8 +32,8 @@ paths: operationId: createUsingPOST consumes: - application/json - produces: - - '*/*' + - application/xml + - application/x-www-form-urlencoded parameters: - in: body name: edRepresentation @@ -42,10 +42,6 @@ paths: schema: $ref: '#/definitions/EntityDescriptorRepresentation' responses: - '200': - description: OK - schema: - type: object '201': description: Created '401': @@ -54,6 +50,8 @@ paths: description: Forbidden '404': description: Not Found + '409': + description: Already Exists deprecated: false '/api/EntityDescriptor/{resourceId}': get: @@ -63,6 +61,7 @@ paths: operationId: getOneUsingGET_1 produces: - application/xml + - application/json parameters: - name: resourceId in: path @@ -89,7 +88,7 @@ paths: consumes: - application/json produces: - - '*/*' + - application/json parameters: - in: body name: edRepresentation @@ -123,7 +122,7 @@ paths: summary: getAll operationId: getAllUsingGET produces: - - '*/*' + - application/json responses: '200': description: OK @@ -143,7 +142,7 @@ paths: summary: search operationId: searchUsingGET produces: - - '*/*' + - application/json parameters: - name: limit in: query @@ -165,8 +164,6 @@ paths: description: Unauthorized '403': description: Forbidden - '404': - description: Not Found deprecated: false /api/MetadataResolvers: get: @@ -175,7 +172,7 @@ paths: summary: getAll operationId: getAllUsingGET_2 produces: - - '*/*' + - application/json responses: '200': description: OK @@ -185,8 +182,6 @@ paths: description: Unauthorized '403': description: Forbidden - '404': - description: Not Found deprecated: false post: tags: @@ -195,8 +190,6 @@ paths: operationId: createUsingPOST_2 consumes: - application/json - produces: - - '*/*' parameters: - in: body name: newResolver @@ -205,10 +198,6 @@ paths: schema: $ref: '#/definitions/MetadataResolver' responses: - '200': - description: OK - schema: - type: object '201': description: Created '401': @@ -217,6 +206,8 @@ paths: description: Forbidden '404': description: Not Found + '409': + description: Already Exists deprecated: false '/api/MetadataResolvers/{metadataResolverId}/Filters': get: @@ -225,7 +216,7 @@ paths: summary: getAll operationId: getAllUsingGET_1 produces: - - '*/*' + - application/json parameters: - name: metadataResolverId in: path @@ -251,8 +242,6 @@ paths: operationId: createUsingPOST_1 consumes: - application/json - produces: - - '*/*' parameters: - in: body name: createdFilter @@ -266,10 +255,6 @@ paths: required: true type: string responses: - '200': - description: OK - schema: - type: object '201': description: Created '401': @@ -286,7 +271,7 @@ paths: summary: getOne operationId: getOneUsingGET_2 produces: - - '*/*' + - application/json parameters: - name: metadataResolverId in: path @@ -318,7 +303,7 @@ paths: consumes: - application/json produces: - - '*/*' + - application/json parameters: - name: metadataResolverId in: path @@ -341,14 +326,14 @@ paths: description: OK schema: type: object - '201': - description: Created '401': description: Unauthorized '403': description: Forbidden '404': description: Not Found + '409': + description: Already Exists deprecated: false '/api/MetadataResolvers/{resourceId}': get: @@ -357,7 +342,7 @@ paths: summary: getOne operationId: getOneUsingGET_3 produces: - - '*/*' + - application/json parameters: - name: resourceId in: path @@ -384,7 +369,7 @@ paths: consumes: - application/json produces: - - '*/*' + - application/json parameters: - name: resourceId in: path @@ -410,6 +395,9 @@ paths: description: Forbidden '404': description: Not Found + '409': + description: Version conflict + deprecated: false '/api/entities/{entityId}': get: @@ -418,7 +406,7 @@ paths: summary: getOne operationId: getOneUsingGET produces: - - '*/*' + - application/json parameters: - name: entityId in: path @@ -457,134 +445,6 @@ paths: '404': description: Not Found deprecated: false - head: - tags: - - metadata-providers-controller - summary: getXml - operationId: getXmlUsingHEAD - consumes: - - application/json - produces: - - application/xml - responses: - '200': - description: OK - schema: - type: object - '204': - description: No Content - '401': - description: Unauthorized - '403': - description: Forbidden - deprecated: false - post: - tags: - - metadata-providers-controller - summary: getXml - operationId: getXmlUsingPOST - consumes: - - application/json - produces: - - application/xml - responses: - '200': - description: OK - schema: - type: object - '201': - description: Created - '401': - description: Unauthorized - '403': - description: Forbidden - '404': - description: Not Found - deprecated: false - put: - tags: - - metadata-providers-controller - summary: getXml - operationId: getXmlUsingPUT - consumes: - - application/json - produces: - - application/xml - responses: - '200': - description: OK - schema: - type: object - '201': - description: Created - '401': - description: Unauthorized - '403': - description: Forbidden - '404': - description: Not Found - deprecated: false - delete: - tags: - - metadata-providers-controller - summary: getXml - operationId: getXmlUsingDELETE - produces: - - application/xml - responses: - '200': - description: OK - schema: - type: object - '204': - description: No Content - '401': - description: Unauthorized - '403': - description: Forbidden - deprecated: false - options: - tags: - - metadata-providers-controller - summary: getXml - operationId: getXmlUsingOPTIONS - consumes: - - application/json - produces: - - application/xml - responses: - '200': - description: OK - schema: - type: object - '204': - description: No Content - '401': - description: Unauthorized - '403': - description: Forbidden - deprecated: false - patch: - tags: - - metadata-providers-controller - summary: getXml - operationId: getXmlUsingPATCH - consumes: - - application/json - produces: - - application/xml - responses: - '200': - description: OK - schema: - type: object - '204': - description: No Content - '401': - description: Unauthorized - '403': - description: Forbidden - deprecated: false definitions: AssertionConsumerServiceRepresentation: type: object From 7951c8abe024015f159214749627396e8be26074 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 16 Jul 2018 15:34:00 -0400 Subject: [PATCH 06/20] SHIBUI-654: when creating resolvers also save attached filters --- .../MetadataResolversController.java | 19 +++++++++----- .../ui/domain/resolvers/MetadataResolver.java | 18 +++++++++++-- ...ResolversControllerIntegrationTests.groovy | 26 ++++++++++++++++++- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java index 5b24f5788..6a92bad2e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java @@ -1,9 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.controller; import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidationService; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -22,8 +24,10 @@ import java.io.IOException; import java.net.URI; +import java.util.List; import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidator.ValidationResult; +import static java.util.stream.Collectors.toList; @RestController @RequestMapping("/api") @@ -36,6 +40,9 @@ public class MetadataResolversController { @Autowired MetadataResolverValidationService metadataResolverValidationService; + @Autowired + private MetadataResolverService metadataResolverService; + @ExceptionHandler({InvalidTypeIdException.class, IOException.class, HttpMessageNotReadableException.class}) public ResponseEntity unableToParseJson(Exception ex) { return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.BAD_REQUEST.toString(), ex.getMessage())); @@ -67,18 +74,19 @@ public ResponseEntity create(@RequestBody MetadataResolver newResolver) { return ResponseEntity.status(HttpStatus.CONFLICT).build(); } - //TODO: we are disregarding attached filters if any sent from UI. - //Only deal with filters via filters endpoints? - newResolver.clearAllFilters(); - ResponseEntity validationErrorResponse = validate(newResolver); if(validationErrorResponse != null) { return validationErrorResponse; } + newResolver.convertFiltersFromTransientRepresentationIfNecessary(); MetadataResolver persistedResolver = resolverRepository.save(newResolver); persistedResolver.updateVersion(); + //TODO: should we call this here? Doing so currently throws ClassCastException + //this.metadataResolverService.reloadFilters(persistedResolver.getName()); + + persistedResolver.convertFiltersIntoTransientRepresentationIfNecessary(); return ResponseEntity.created(getResourceUriFor(persistedResolver)).body(persistedResolver); } @@ -102,8 +110,7 @@ public ResponseEntity update(@PathVariable String resourceId, @RequestBody Me updatedResolver.setAudId(existingResolver.getAudId()); - //TODO: we are disregarding attached filters if any sent from UI. - //Only deal with filters via filters endpoints? + //If one needs to update filters, it should be dealt with via filters endpoints updatedResolver.setMetadataFilters(existingResolver.getMetadataFilters()); MetadataResolver persistedResolver = resolverRepository.save(updatedResolver); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java index e5b1221c1..d1f0e97f6 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -23,6 +24,8 @@ import java.util.List; import java.util.UUID; +import static java.util.stream.Collectors.toList; + @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @EqualsAndHashCode(callSuper = true, exclude = {"version"}) @@ -71,7 +74,18 @@ public void updateVersion() { this.version = hashCode(); } - public void clearAllFilters() { - this.metadataFilters.clear(); + public void convertFiltersIntoTransientRepresentationIfNecessary() { + getAvailableEntityAttributesFilters().forEach(EntityAttributesFilter::intoTransientRepresentation); + } + + public void convertFiltersFromTransientRepresentationIfNecessary() { + getAvailableEntityAttributesFilters().forEach(EntityAttributesFilter::fromTransientRepresentation); + } + + private List getAvailableEntityAttributesFilters() { + return this.metadataFilters.stream() + .filter(EntityAttributesFilter.class::isInstance) + .map(EntityAttributesFilter.class::cast) + .collect(toList()); } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy index cb6592990..25071cb98 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy @@ -2,6 +2,8 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository @@ -53,6 +55,7 @@ class MetadataResolversControllerIntegrationTests extends Specification { generator = new TestObjectGenerator(attributeUtility) mapper = new ObjectMapper() mapper.enable(SerializationFeature.INDENT_OUTPUT) + mapper.registerModule(new JavaTimeModule()) } def cleanup() { @@ -148,7 +151,7 @@ class MetadataResolversControllerIntegrationTests extends Specification { } @Unroll - def "POST new DynamicHttpMetadataResolver of type #resolverType -> /api/MetadataResolvers"(String resolverType) { + def "POST new concrete MetadataResolver of type #resolverType -> /api/MetadataResolvers"(String resolverType) { given: 'New MetadataResolver JSON representation' def resolver = generator.buildRandomMetadataResolverOfType(resolverType) @@ -231,6 +234,27 @@ class MetadataResolversControllerIntegrationTests extends Specification { updatedResult.statusCodeValue == 409 } + def "POST new MetadataResolver with one EntityAttributesFilters attached -> /api/MetadataResolvers"() { + given: 'New MetadataResolver with attached entity attributes filter JSON representation' + def resolver = generator.buildRandomMetadataResolverOfType('FileBacked') + resolver.metadataFilters << generator.entityAttributesFilter() + + when: 'POST request is made with new FileBackedMetadataResolver with EntityAttributesFilter JSON representation' + def result = this.restTemplate.postForEntity(BASE_URI, createRequestHttpEntityFor { mapper.writeValueAsString(resolver) }, String) + + then: + result.statusCodeValue == 201 + result.headers.Location[0].contains(BASE_URI) + + when: 'Query REST API for newly created resolver' + def createdResolverResult = this.restTemplate.getForEntity(result.headers.Location[0], String) + def createdResolver = mapper.readValue(createdResolverResult.body, edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver) + + then: + createdResolver.metadataFilters.size() == 1 + createdResolver.metadataFilters[0] instanceof EntityAttributesFilter + } + private HttpEntity createRequestHttpEntityFor(Closure jsonBodySupplier) { new HttpEntity(jsonBodySupplier(), ['Content-Type': 'application/json'] as HttpHeaders) } From 334d81e07286f4fd69bcd615161845debe27d80e Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 17 Jul 2018 09:15:42 -0400 Subject: [PATCH 07/20] SHIBUI-654: polishing --- .../admin/ui/controller/MetadataResolversController.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java index 6a92bad2e..b94125889 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java @@ -83,9 +83,6 @@ public ResponseEntity create(@RequestBody MetadataResolver newResolver) { MetadataResolver persistedResolver = resolverRepository.save(newResolver); persistedResolver.updateVersion(); - //TODO: should we call this here? Doing so currently throws ClassCastException - //this.metadataResolverService.reloadFilters(persistedResolver.getName()); - persistedResolver.convertFiltersIntoTransientRepresentationIfNecessary(); return ResponseEntity.created(getResourceUriFor(persistedResolver)).body(persistedResolver); } From 517c90b30c38e9769796096e2a31d7de1e145481 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 17 Jul 2018 09:22:17 -0400 Subject: [PATCH 08/20] SHIBUI-654: polishing --- .../admin/ui/controller/MetadataResolversController.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java index b94125889..1d6c51321 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java @@ -1,11 +1,9 @@ package edu.internet2.tier.shibboleth.admin.ui.controller; import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; -import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidationService; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; -import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -24,10 +22,8 @@ import java.io.IOException; import java.net.URI; -import java.util.List; import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidator.ValidationResult; -import static java.util.stream.Collectors.toList; @RestController @RequestMapping("/api") @@ -40,9 +36,6 @@ public class MetadataResolversController { @Autowired MetadataResolverValidationService metadataResolverValidationService; - @Autowired - private MetadataResolverService metadataResolverService; - @ExceptionHandler({InvalidTypeIdException.class, IOException.class, HttpMessageNotReadableException.class}) public ResponseEntity unableToParseJson(Exception ex) { return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.BAD_REQUEST.toString(), ex.getMessage())); From 70c39cd9ee8d77748bb31d9f8c4c672647394c09 Mon Sep 17 00:00:00 2001 From: Bill Smith Date: Tue, 17 Jul 2018 17:59:06 -0700 Subject: [PATCH 09/20] [SHIBUI-635] Initial support for i18n. Controller, configuration, tests, properties. --- .../CoreShibUiConfiguration.java | 21 ++++ .../InternationalizationConfiguration.java | 25 ++++ ...nternationalizationMessagesController.java | 25 ++++ .../MappedResourceBundleMessageSource.java | 28 +++++ .../main/resources/i18n/messages.properties | 10 ++ .../resources/i18n/messages_en.properties | 1 + .../resources/i18n/messages_fr.properties | 1 + ...ionalizationMessagesControllerTests.groovy | 113 ++++++++++++++++++ .../test/resources/i18n/messages.properties | 1 + .../resources/i18n/messages_en.properties | 1 + .../resources/i18n/messages_fr.properties | 1 + 11 files changed, 227 insertions(+) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/InternationalizationConfiguration.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/InternationalizationMessagesController.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/i18n/MappedResourceBundleMessageSource.java create mode 100644 backend/src/main/resources/i18n/messages.properties create mode 100644 backend/src/main/resources/i18n/messages_en.properties create mode 100644 backend/src/main/resources/i18n/messages_fr.properties create mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/InternationalizationMessagesControllerTests.groovy create mode 100644 backend/src/test/resources/i18n/messages.properties create mode 100644 backend/src/test/resources/i18n/messages_en.properties create mode 100644 backend/src/test/resources/i18n/messages_fr.properties diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index edcfc4ccb..ca7be77ea 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -21,8 +21,12 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.util.UrlPathHelper; import javax.servlet.http.HttpServletRequest; @@ -78,6 +82,11 @@ public AttributeUtility attributeUtility() { @Autowired Directory directory; + @Autowired + LocaleResolver localeResolver; + + @Autowired + ResourceBundleMessageSource messageSource; @Bean public EntityDescriptorFilesScheduledTasks entityDescriptorFilesScheduledTasks(EntityDescriptorRepository entityDescriptorRepository) { @@ -103,6 +112,13 @@ public EntityIdsSearchService entityIdsSearchService() { }; } + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() { + LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); + localeChangeInterceptor.setParamName("lang"); + return localeChangeInterceptor; + } + /** * A WebMvcConfigurer that won't mangle the path for the entities endpoint. * @@ -139,6 +155,11 @@ public String getOriginatingServletPath(HttpServletRequest request) { helper.setUrlDecode(false); configurer.setUrlPathHelper(helper); } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + } }; } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/InternationalizationConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/InternationalizationConfiguration.java new file mode 100644 index 000000000..dc8599865 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/InternationalizationConfiguration.java @@ -0,0 +1,25 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration; + +import edu.internet2.tier.shibboleth.admin.ui.i18n.MappedResourceBundleMessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +@Configuration +public class InternationalizationConfiguration { + @Bean + public LocaleResolver localeResolver() { + // TODO if we want to control the order, we can implement our own locale resolver instead of using the SessionLocaleResolver. + SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver(); + return sessionLocaleResolver; + } + + @Bean + public MappedResourceBundleMessageSource messageSource() { + MappedResourceBundleMessageSource source = new MappedResourceBundleMessageSource(); + source.setBasenames("i18n/messages"); + source.setUseCodeAsDefaultMessage(true); + return source; + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/InternationalizationMessagesController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/InternationalizationMessagesController.java new file mode 100644 index 000000000..566dbe4a3 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/InternationalizationMessagesController.java @@ -0,0 +1,25 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller; + +import edu.internet2.tier.shibboleth.admin.ui.i18n.MappedResourceBundleMessageSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.Locale; + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@Controller +@RequestMapping(value = "/api/messages") +public class InternationalizationMessagesController { + @Autowired + MappedResourceBundleMessageSource messageSource; + + @GetMapping + public ResponseEntity getAll(Locale locale) { + return ResponseEntity.ok(messageSource.getMessagesMap(locale)); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/i18n/MappedResourceBundleMessageSource.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/i18n/MappedResourceBundleMessageSource.java new file mode 100644 index 000000000..672cca880 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/i18n/MappedResourceBundleMessageSource.java @@ -0,0 +1,28 @@ +package edu.internet2.tier.shibboleth.admin.ui.i18n; + +import org.springframework.context.support.ResourceBundleMessageSource; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +public class MappedResourceBundleMessageSource extends ResourceBundleMessageSource { + public Map getMessagesMap(Locale locale) { + ResourceBundle resourceBundle = this.doGetBundle("i18n/messages", locale); + Map messagesMap = new HashMap<>(); + Enumeration bundleKeys = resourceBundle.getKeys(); + + while (bundleKeys.hasMoreElements()) { + String key = (String)bundleKeys.nextElement(); + String value = resourceBundle.getString(key); + messagesMap.put(key, value); + } + + return messagesMap; + } +} diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties new file mode 100644 index 000000000..d9a5d0df4 --- /dev/null +++ b/backend/src/main/resources/i18n/messages.properties @@ -0,0 +1,10 @@ +# Fill this file with key/value pairs, as follows: +# +# some.test.message=This is a test message. +# +# Then, create a copy using the name of the language code: +# +# messages_.properties +# +# Do this for each language we want to support. +# Ideally, all messages should exist for each language. diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties new file mode 100644 index 000000000..5c101832f --- /dev/null +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -0,0 +1 @@ +a.sample.message=This is a sample message. \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_fr.properties b/backend/src/main/resources/i18n/messages_fr.properties new file mode 100644 index 000000000..750c5fe51 --- /dev/null +++ b/backend/src/main/resources/i18n/messages_fr.properties @@ -0,0 +1 @@ +a.sample.message=Le francais est tres difficile. \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/InternationalizationMessagesControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/InternationalizationMessagesControllerTests.groovy new file mode 100644 index 000000000..48a536d19 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/InternationalizationMessagesControllerTests.groovy @@ -0,0 +1,113 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration +import edu.internet2.tier.shibboleth.admin.ui.i18n.MappedResourceBundleMessageSource +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.web.servlet.LocaleResolver +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor +import spock.lang.Specification + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@DataJpaTest +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class InternationalizationMessagesControllerTests extends Specification { + @Autowired + MappedResourceBundleMessageSource messageSource + + @Autowired + LocaleChangeInterceptor localeChangeInterceptor + + @Autowired + LocaleResolver localResolver + + def controller + def mockMvc + + def setup() { + controller = new InternationalizationMessagesController( + messageSource: messageSource + ) + + mockMvc = MockMvcBuilders.standaloneSetup(controller) + .setLocaleResolver(localResolver) + .addInterceptors(localeChangeInterceptor) + .build() + } + + def messagesUrl = "/api/messages" + + def expectedEnglishResult = + '{' + + ' "some.test.message": "This is the English test message."' + + '}' + + def expectedFrenchResult = + '{' + + ' "some.test.message": "Je ne sais pas Francais."' + + '}' + + def "GET messages with no header or \"lang\" param defaults to returning english messages"() { + when: + def result = mockMvc.perform( + get(messagesUrl)) + + then: + result.andExpect(content().json(expectedEnglishResult)) + } + + def "GET messages with Accept-Language returns messages in that language"() { + when: + def result = mockMvc.perform( + get(messagesUrl) + .header("Accept-Language", "fr")) + + then: + result.andExpect(content().json(expectedFrenchResult)) + } + + def "GET messages with \"lang\" request param returns messages in that language"() { + when: + def result = mockMvc.perform( + get(messagesUrl) + .param("lang", "fr")) + + then: + result.andExpect(content().json(expectedFrenchResult)) + } + + def "GET messages with both Accept-Language header and \"lang\" request param returns messages in the language specified by the \"lang\" parameter"() { + when: + def result = mockMvc.perform( + get(messagesUrl) + .header("Accept-Language", "en") + .param("lang", "fr")) + + then: + result.andExpect(content().json(expectedFrenchResult)) + } + + def "GET messages with an unsupported Accept-Language returns the default language"() { + when: + def result = mockMvc.perform( + get(messagesUrl) + .header("Accept-Language", "es")) + + then: + result.andExpect(content().json(expectedEnglishResult)) + } +} diff --git a/backend/src/test/resources/i18n/messages.properties b/backend/src/test/resources/i18n/messages.properties new file mode 100644 index 000000000..3e763c8d5 --- /dev/null +++ b/backend/src/test/resources/i18n/messages.properties @@ -0,0 +1 @@ +some.test.message=This is the default message. \ No newline at end of file diff --git a/backend/src/test/resources/i18n/messages_en.properties b/backend/src/test/resources/i18n/messages_en.properties new file mode 100644 index 000000000..3a05d1b5c --- /dev/null +++ b/backend/src/test/resources/i18n/messages_en.properties @@ -0,0 +1 @@ +some.test.message=This is the English test message. \ No newline at end of file diff --git a/backend/src/test/resources/i18n/messages_fr.properties b/backend/src/test/resources/i18n/messages_fr.properties new file mode 100644 index 000000000..23ded1634 --- /dev/null +++ b/backend/src/test/resources/i18n/messages_fr.properties @@ -0,0 +1 @@ +some.test.message=Je ne sais pas Francais. \ No newline at end of file From 7106751a9165130eeccd5bed5f4d3714e41e1574 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Wed, 18 Jul 2018 14:13:06 -0400 Subject: [PATCH 10/20] SHIBUI-621: Introduce xmlId --- .../ui/service/JPAMetadataResolverServiceImpl.groovy | 10 +++++----- .../admin/ui/domain/resolvers/MetadataResolver.java | 3 +++ .../IncommonJPAMetadataResolverServiceImplTests.groovy | 4 +--- .../service/JPAMetadataResolverServiceImplTests.groovy | 4 ++-- .../admin/ui/util/TestObjectGenerator.groovy | 5 +++++ 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy index 34029e28a..a9485073b 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -163,7 +163,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } void constructXmlNodeForResolver(FilesystemMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { - markupBuilderDelegate.MetadataProvider(id: resolver.name, + markupBuilderDelegate.MetadataProvider(id: resolver.xmlId, 'xsi:type': 'FilesystemMetadataProvider', metadataFile: resolver.metadataFile, @@ -187,7 +187,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } void constructXmlNodeForResolver(DynamicHttpMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { - markupBuilderDelegate.MetadataProvider(id: resolver.name, + markupBuilderDelegate.MetadataProvider(id: resolver.xmlId, 'xsi:type': 'DynamicHttpMetadataProvider', requireValidMetadata: !resolver.requireValidMetadata ?: null, failFastInitialization: !resolver.failFastInitialization ?: null, @@ -234,7 +234,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } void constructXmlNodeForResolver(FileBackedHttpMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { - markupBuilderDelegate.MetadataProvider(id: resolver.name, + markupBuilderDelegate.MetadataProvider(id: resolver.xmlId, 'xsi:type': 'FileBackedHTTPMetadataProvider', backingFile: resolver.backingFile, metadataURL: resolver.metadataURL, @@ -279,7 +279,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { sourceManagerRef: resolver.sourceManagerRef, sourceKeyGeneratorRef: resolver.sourceKeyGeneratorRef, - id: resolver.name, + id: resolver.xmlId, 'xsi:type': 'DynamicHttpMetadataProvider', requireValidMetadata: !resolver.requireValidMetadata ?: null, failFastInitialization: !resolver.failFastInitialization ?: null, @@ -314,7 +314,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { def resourceType = resolver.validateAndDetermineResourceType() markupBuilderDelegate.MetadataProvider( - id: resolver.name, + id: resolver.xmlId, 'xsi:type': 'ResourceBackedMetadataProvider', parserPoolRef: resolver.reloadableMetadataResolverAttributes?.parserPoolRef, minRefreshDelay: resolver.reloadableMetadataResolverAttributes?.minRefreshDelay, diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java index d1f0e97f6..ab8eda99f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java @@ -51,6 +51,9 @@ public class MetadataResolver extends AbstractAuditable { @Column(unique = true) private String resourceId = UUID.randomUUID().toString(); + @Column(unique = true) + private String xmlId; + private Boolean requireValidMetadata = true; private Boolean failFastInitialization = true; diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy index 24a97ccdb..853ad36c1 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy @@ -130,9 +130,7 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { if (!metadataResolverRepository.findAll().iterator().hasNext()) { //Generate and test edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver. Add more as // we implement them - def mr = new TestObjectGenerator(attributeUtility).fileBackedHttpMetadataResolver() - mr.setName("HTTPMetadata") - metadataResolverRepository.save(mr) + metadataResolverRepository.save(new TestObjectGenerator(attributeUtility).fileBackedHttpMetadataResolver()) // Generate and test edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver. metadataResolverRepository.save(new TestObjectGenerator(attributeUtility).dynamicHttpMetadataResolver()) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy index 9c367c1d6..bbcb7f5c6 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy @@ -176,7 +176,7 @@ class JPAMetadataResolverServiceImplTests extends Specification { def 'test generating ResourceBackedMetadataResolver with SVN resource type xml snippet'() { given: def resolver = new edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver().with { - it.name = 'SVNResourceMetadata' + it.xmlId = 'SVNResourceMetadata' it.svnMetadataResource = new SvnMetadataResource().with { it.resourceFile = 'entity.xml' it.repositoryURL = 'https://svn.example.org/repo/path/to.dir' @@ -198,7 +198,7 @@ class JPAMetadataResolverServiceImplTests extends Specification { def 'test generating ResourceBackedMetadataResolver with classpath resource type xml snippet'() { given: def resolver = new edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver().with { - it.name = 'ClasspathResourceMetadata' + it.xmlId = 'ClasspathResourceMetadata' it.classpathMetadataResource = new ClasspathMetadataResource().with { it.file = '/path/to/a/classpath/location/metadata.xml' it diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy index 4652fe361..4469e1b36 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy @@ -403,6 +403,7 @@ class TestObjectGenerator { FilesystemMetadataResolver filesystemMetadataResolver() { new FilesystemMetadataResolver().with { it.name = 'FilesystemMetadata' + it.xmlId = 'FilesystemMetadata' it.metadataFile = 'some metadata filename' it.reloadableMetadataResolverAttributes = new ReloadableMetadataResolverAttributes().with { @@ -418,6 +419,7 @@ class TestObjectGenerator { FileBackedHttpMetadataResolver fileBackedHttpMetadataResolver() { new FileBackedHttpMetadataResolver().with { it.name = 'HTTPMetadata' + it.xmlId = 'HTTPMetadata' it.backingFile = '%{idp.home}/metadata/incommonmd.xml' it.metadataURL = 'http://md.incommon.org/InCommon/InCommon-metadata.xml' @@ -434,6 +436,7 @@ class TestObjectGenerator { DynamicHttpMetadataResolver dynamicHttpMetadataResolver() { new DynamicHttpMetadataResolver().with { it.name = 'DynamicHTTP' + it.xmlId = 'DynamicHTTP' it } } @@ -441,6 +444,7 @@ class TestObjectGenerator { LocalDynamicMetadataResolver localDynamicMetadataResolver() { new LocalDynamicMetadataResolver().with { it.name = 'LocalDynamic' + it.xmlId = 'LocalDynamic' it } } @@ -448,6 +452,7 @@ class TestObjectGenerator { ResourceBackedMetadataResolver resourceBackedMetadataResolverForSVN() { new ResourceBackedMetadataResolver().with { it.name = 'SVNResourceMetadata' + it.xmlId = 'SVNResourceMetadata' it.svnMetadataResource = new SvnMetadataResource().with { it.resourceFile = 'entity.xml' it.repositoryURL = 'https://svn.example.org/repo/path/to.dir' From 005f2f757eaf8911a58b68eef4b6a215d243d12d Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Thu, 19 Jul 2018 11:56:16 -0400 Subject: [PATCH 11/20] SHIBUI-669: clean up the dead code --- ...DynamicHttpMetadataProviderController.java | 111 ------- ...eBackedHttpMetadataProviderController.java | 110 ------- ...ocalDynamicMetadataProviderController.java | 110 ------- ...HttpMetadataProviderControllerTests.groovy | 272 ----------------- ...HttpMetadataProviderControllerTests.groovy | 274 ----------------- ...amicMetadataProviderControllerTests.groovy | 276 ------------------ 6 files changed, 1153 deletions(-) delete mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java delete mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java delete mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java delete mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy delete mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy delete mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java deleted file mode 100644 index 866f54231..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java +++ /dev/null @@ -1,111 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller; - -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver; -import edu.internet2.tier.shibboleth.admin.ui.repository.DynamicHttpMetadataResolverRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; - -import java.net.URI; - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@RestController -@RequestMapping("/api/MetadataProvider/DynamicHttp") -public class DynamicHttpMetadataProviderController { - private static final Logger logger = LoggerFactory.getLogger(DynamicHttpMetadataProviderController.class); - - @Autowired - DynamicHttpMetadataResolverRepository repository; - - @DeleteMapping("/{resourceId}") - public ResponseEntity deleteByResourceId(@PathVariable String resourceId) { - if (repository.deleteByResourceId(resourceId)) { - return ResponseEntity.accepted().build(); - } else { - return ResponseEntity.notFound().build(); - } - } - - @GetMapping("/name/{metadataProviderName}") - @Transactional(readOnly = true) - public ResponseEntity getOneByName(@PathVariable String metadataProviderName) { - DynamicHttpMetadataResolver resolver = repository.findByName(metadataProviderName); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @GetMapping("/{resourceId}") - @Transactional(readOnly = true) - public ResponseEntity getOneByResourceId(@PathVariable String resourceId) { - DynamicHttpMetadataResolver resolver = repository.findByResourceId(resourceId); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @PostMapping - public ResponseEntity create(@RequestBody DynamicHttpMetadataResolver resolver) { - if (repository.findByName(resolver.getName()) != null) { - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - DynamicHttpMetadataResolver persistedResolver = repository.save(resolver); - persistedResolver.setVersion(persistedResolver.hashCode()); - - return ResponseEntity - .created(getResourceUriFor(persistedResolver)) - .body(persistedResolver); - } - - @PutMapping - public ResponseEntity update(@RequestBody DynamicHttpMetadataResolver resolver) { - DynamicHttpMetadataResolver existingResolver = repository.findByResourceId(resolver.getResourceId()); - - if (existingResolver == null) { - return ResponseEntity.notFound().build(); - } - - if (existingResolver.hashCode() != resolver.getVersion()) { - logger.info("Comparing: " + existingResolver.hashCode() + " with " + resolver.getVersion()); - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - resolver.setAudId(existingResolver.getAudId()); - //TODO: Do we need to set anything else? dates? - - DynamicHttpMetadataResolver updatedResolver = repository.save(resolver); - updatedResolver.setVersion(updatedResolver.hashCode()); - - return ResponseEntity.ok(updatedResolver); - } - - private static URI getResourceUriFor(DynamicHttpMetadataResolver resolver) { - return ServletUriComponentsBuilder - .fromCurrentServletMapping().path("/api/MetadataProvider/DynamicHttp/") - .pathSegment(resolver.getResourceId()) - .build() - .toUri(); - } - -} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java deleted file mode 100644 index 5b8000482..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java +++ /dev/null @@ -1,110 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller; - -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver; -import edu.internet2.tier.shibboleth.admin.ui.repository.FileBackedHttpMetadataResolverRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; - -import java.net.URI; - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@RestController -@RequestMapping("/api/MetadataProvider/FileBackedHttp") -public class FileBackedHttpMetadataProviderController { - private static final Logger logger = LoggerFactory.getLogger(FileBackedHttpMetadataProviderController.class); - - @Autowired - FileBackedHttpMetadataResolverRepository repository; - - @DeleteMapping("/{resourceId}") - public ResponseEntity deleteByResourceId(@PathVariable String resourceId) { - if (repository.deleteByResourceId(resourceId)) { - return ResponseEntity.accepted().build(); - } else { - return ResponseEntity.notFound().build(); - } - } - - @GetMapping("/name/{metadataProviderName}") - @Transactional(readOnly = true) - public ResponseEntity getOneByName(@PathVariable String metadataProviderName) { - FileBackedHttpMetadataResolver resolver = repository.findByName(metadataProviderName); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @GetMapping("/{resourceId}") - @Transactional(readOnly = true) - public ResponseEntity getOneByResourceId(@PathVariable String resourceId) { - FileBackedHttpMetadataResolver resolver = repository.findByResourceId(resourceId); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @PostMapping - public ResponseEntity create(@RequestBody FileBackedHttpMetadataResolver resolver) { - if (repository.findByName(resolver.getName()) != null) { - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - FileBackedHttpMetadataResolver persistedResolver = repository.save(resolver); - persistedResolver.setVersion(persistedResolver.hashCode()); - - return ResponseEntity - .created(getResourceUriFor(persistedResolver)) - .body(persistedResolver); - } - - @PutMapping - public ResponseEntity update(@RequestBody FileBackedHttpMetadataResolver resolver) { - FileBackedHttpMetadataResolver existingResolver = repository.findByResourceId(resolver.getResourceId()); - - if (existingResolver == null) { - return ResponseEntity.notFound().build(); - } - - if (existingResolver.hashCode() != resolver.getVersion()) { - logger.info("Comparing: " + existingResolver.hashCode() + " with " + resolver.getVersion()); - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - resolver.setAudId(existingResolver.getAudId()); - //TODO: Do we need to set anything else? dates? - - FileBackedHttpMetadataResolver updatedResolver = repository.save(resolver); - updatedResolver.setVersion(updatedResolver.hashCode()); - - return ResponseEntity.ok(updatedResolver); - } - - private static URI getResourceUriFor(FileBackedHttpMetadataResolver resolver) { - return ServletUriComponentsBuilder - .fromCurrentServletMapping().path("/api/MetadataProvider/FileBackedHttp/") - .pathSegment(resolver.getResourceId()) - .build() - .toUri(); - } -} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java deleted file mode 100644 index 2d3a104ee..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java +++ /dev/null @@ -1,110 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller; - -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver; -import edu.internet2.tier.shibboleth.admin.ui.repository.LocalDynamicMetadataResolverRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; - -import java.net.URI; - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@RestController -@RequestMapping("/api/MetadataProvider/LocalDynamic") -public class LocalDynamicMetadataProviderController { - private static final Logger logger = LoggerFactory.getLogger(LocalDynamicMetadataProviderController.class); - - @Autowired - LocalDynamicMetadataResolverRepository repository; - - @DeleteMapping("/{resourceId}") - public ResponseEntity deleteByResourceId(@PathVariable String resourceId) { - if (repository.deleteByResourceId(resourceId)) { - return ResponseEntity.accepted().build(); - } else { - return ResponseEntity.notFound().build(); - } - } - - @GetMapping("/name/{metadataProviderName}") - @Transactional(readOnly = true) - public ResponseEntity getOneByName(@PathVariable String metadataProviderName) { - LocalDynamicMetadataResolver resolver = repository.findByName(metadataProviderName); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @GetMapping("/{resourceId}") - @Transactional(readOnly = true) - public ResponseEntity getOneByResourceId(@PathVariable String resourceId) { - LocalDynamicMetadataResolver resolver = repository.findByResourceId(resourceId); - if (resolver == null) { - return ResponseEntity.notFound().build(); - } else { - resolver.setVersion(resolver.hashCode()); - return ResponseEntity.ok(resolver); - } - } - - @PostMapping - public ResponseEntity create(@RequestBody LocalDynamicMetadataResolver resolver) { - if (repository.findByName(resolver.getName()) != null) { - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - LocalDynamicMetadataResolver persistedResolver = repository.save(resolver); - persistedResolver.setVersion(persistedResolver.hashCode()); - - return ResponseEntity - .created(getResourceUriFor(persistedResolver)) - .body(persistedResolver); - } - - @PutMapping - public ResponseEntity update(@RequestBody LocalDynamicMetadataResolver resolver) { - LocalDynamicMetadataResolver existingResolver = repository.findByResourceId(resolver.getResourceId()); - - if (existingResolver == null) { - return ResponseEntity.notFound().build(); - } - - if (existingResolver.hashCode() != resolver.getVersion()) { - logger.info("Comparing: " + existingResolver.hashCode() + " with " + resolver.getVersion()); - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - resolver.setAudId(existingResolver.getAudId()); - //TODO: Do we need to set anything else? dates? - - LocalDynamicMetadataResolver updatedResolver = repository.save(resolver); - updatedResolver.setVersion(updatedResolver.hashCode()); - - return ResponseEntity.ok(updatedResolver); - } - - private static URI getResourceUriFor(LocalDynamicMetadataResolver resolver) { - return ServletUriComponentsBuilder - .fromCurrentServletMapping().path("/api/MetadataProvider/LocalDynamic/") - .pathSegment(resolver.getResourceId()) - .build() - .toUri(); - } -} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy deleted file mode 100644 index 5b10c9f6e..000000000 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy +++ /dev/null @@ -1,272 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller - -import com.fasterxml.jackson.databind.ObjectMapper -import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration -import edu.internet2.tier.shibboleth.admin.ui.repository.DynamicHttpMetadataResolverRepository -import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator -import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator -import edu.internet2.tier.shibboleth.admin.util.AttributeUtility -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest -import org.springframework.data.jpa.repository.config.EnableJpaRepositories -import org.springframework.test.context.ContextConfiguration -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import spock.lang.Specification - -import static org.hamcrest.CoreMatchers.containsString -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.* - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) -@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) -@EntityScan("edu.internet2.tier.shibboleth.admin.ui") -class DynamicHttpMetadataProviderControllerTests extends Specification { - RandomGenerator randomGenerator - TestObjectGenerator testObjectGenerator - ObjectMapper mapper - - def repository = Mock(DynamicHttpMetadataResolverRepository) - def controller - def mockMvc - - @Autowired - AttributeUtility attributeUtility - - def setup() { - randomGenerator = new RandomGenerator() - testObjectGenerator = new TestObjectGenerator(attributeUtility) - mapper = new ObjectMapper() - - controller = new DynamicHttpMetadataProviderController ( - repository: repository - ) - - mockMvc = MockMvcBuilders.standaloneSetup(controller).build() - } - - def "DELETE deletes the desired resolver"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> true - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/DynamicHttp/$randomResourceId")) - - then: - result.andExpect(status().isAccepted()) - } - - def "DELETE returns error when desired resolver is not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> false - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/DynamicHttp/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "POST a new resolver properly persists and returns the new persisted resolver"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - resolver.setVersion(resolver.hashCode()) - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolver.getName()) >> null - 1 * repository.save(_) >> resolver - - def expectedResolverUUID = resolver.getResourceId() - def expectedResponseHeader = 'Location' - def expectedResponseHeaderValue = "/api/MetadataProvider/DynamicHttp/$expectedResolverUUID" - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isCreated()) - .andExpect(content().json(postedJsonBody, false)) - .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) - - } - - def "POST a new resolver that has a name of a persisted resolver returns conflict"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - def resolverName = resolver.name - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolverName) >> resolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "GET by resourceId returns the desired persisted resolver"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - resolver.version = resolver.hashCode() - def resourceId = resolver.resourceId - def resolverJson = mapper.writeValueAsString(resolver) - - 1 * repository.findByResourceId(resourceId) >> resolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/DynamicHttp/$resourceId")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resource id returns not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.findByResourceId(randomResourceId) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/DynamicHttp/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "GET by resolver name returns the desired persisted resolver"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - resolver.version = resolver.hashCode() - def resolverName = resolver.name - def resolverJson = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolverName) >> resolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/DynamicHttp/name/$resolverName")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resolver name returns not found"() { - given: - def randomResolverName = randomGenerator.randomString(10) - - 1 * repository.findByName(randomResolverName) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/DynamicHttp/name/$randomResolverName")) - - then: - result.andExpect(status().isNotFound()) - } - - def "PUT allows for a successful update of an already-persisted resolver"() { - given: - def existingResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def resourceId = existingResolver.resourceId - def existingResolverJson = mapper.writeValueAsString(existingResolver) - - def updatedResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - updatedResolver.resourceId = existingResolver.resourceId - updatedResolver.version = existingResolver.version - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(existingResolver.resourceId) >> existingResolver - 1 * repository.save(_) >> updatedResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - def expectedJson = new JsonSlurper().parseText(postedJsonBody) - expectedJson << [version: updatedResolver.hashCode()] - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(JsonOutput.toJson(expectedJson), false)) - } - - def "PUT of an updated resolver with an incorrect version returns a conflict"() { - given: - def existingResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - - def updatedResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - updatedResolver.resourceId = existingResolver.resourceId - updatedResolver.version = updatedResolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(existingResolver.resourceId) >> existingResolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "PUT of a resolver that is not persisted returns not found"() { - given: - def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByResourceId(resolver.resourceId) >> null - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/DynamicHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isNotFound()) - } -} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy deleted file mode 100644 index 99457a335..000000000 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy +++ /dev/null @@ -1,274 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller - -import com.fasterxml.jackson.databind.ObjectMapper -import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration -import edu.internet2.tier.shibboleth.admin.ui.repository.FileBackedHttpMetadataResolverRepository -import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator -import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator -import edu.internet2.tier.shibboleth.admin.util.AttributeUtility -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest -import org.springframework.data.jpa.repository.config.EnableJpaRepositories -import org.springframework.test.context.ContextConfiguration -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import spock.lang.Specification - -import static org.hamcrest.CoreMatchers.containsString -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.* - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) -@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) -@EntityScan("edu.internet2.tier.shibboleth.admin.ui") -class FileBackedHttpMetadataProviderControllerTests extends Specification { - RandomGenerator randomGenerator - TestObjectGenerator testObjectGenerator - ObjectMapper mapper - - @Autowired - AttributeUtility attributeUtility - - def repository = Mock(FileBackedHttpMetadataResolverRepository) - def controller - def mockMvc - - def setup() { - randomGenerator = new RandomGenerator() - testObjectGenerator = new TestObjectGenerator(attributeUtility) - mapper = new ObjectMapper() - - controller = new FileBackedHttpMetadataProviderController ( - repository: repository - ) - - mockMvc = MockMvcBuilders.standaloneSetup(controller).build() - } - - def "DELETE deletes the desired resolver"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> true - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) - - then: - result.andExpect(status().isAccepted()) - } - - def "DELETE returns error when desired resolver is not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> false - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "POST a new resolver properly persists and returns the new persisted resolver"() { - given: - def resolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - resolver.version = resolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolver.getName()) >> null - 1 * repository.save(_) >> resolver - - def expectedResolverUUID = resolver.getResourceId() - def expectedResponseHeader = 'Location' - def expectedResponseHeaderValue = "/api/MetadataProvider/FileBackedHttp/$expectedResolverUUID" - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isCreated()) - .andExpect(content().json(postedJsonBody, false)) - .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) - - } - - def "POST a new resolver that has a name of a persisted resolver returns conflict"() { - given: - def existingResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - def randomResolverName = existingResolver.name - def newResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - newResolver.name = randomResolverName - def postedJsonBody = mapper.writeValueAsString(newResolver) - - 1 * repository.findByName(randomResolverName) >> existingResolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "GET by resourceId returns the desired persisted resolver"() { - given: - def existingResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def randomResourceId = existingResolver.resourceId - def resolverJson = mapper.writeValueAsString(existingResolver) - - 1 * repository.findByResourceId(randomResourceId) >> existingResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resource id returns not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.findByResourceId(randomResourceId) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "GET by resolver name returns the desired persisted resolver"() { - given: - def randomResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - randomResolver.version = randomResolver.hashCode() - def randomResolverName = randomResolver.name - def resolverJson = mapper.writeValueAsString(randomResolver) - - 1 * repository.findByName(randomResolverName) >> randomResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/FileBackedHttp/name/$randomResolverName")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resolver name returns not found"() { - given: - def randomResolverName = randomGenerator.randomString(10) - - 1 * repository.findByName(randomResolverName) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/FileBackedHttp/name/$randomResolverName")) - - then: - result.andExpect(status().isNotFound()) - } - - def "PUT allows for a successful update of an already-persisted resolver"() { - given: - def existingResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def randomResourceId = existingResolver.resourceId - def updatedResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - updatedResolver.version = existingResolver.version - updatedResolver.resourceId = existingResolver.resourceId - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - - 1 * repository.findByResourceId(randomResourceId) >> existingResolver - 1 * repository.save(_) >> updatedResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - def expectedJson = new JsonSlurper().parseText(postedJsonBody) - expectedJson << [version: updatedResolver.hashCode()] - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(JsonOutput.toJson(expectedJson), false)) - } - - def "PUT of an updated resolver with an incorrect version returns a conflict"() { - given: - def existingResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def randomResourceId = existingResolver.resourceId - def updatedResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - updatedResolver.version = updatedResolver.hashCode() - updatedResolver.resourceId = existingResolver.resourceId - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(randomResourceId) >> existingResolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "PUT of a resolver that is not persisted returns not found"() { - given: - def randomResolver = testObjectGenerator.buildFileBackedHttpMetadataResolver() - randomResolver.version = randomResolver.hashCode() - def randomResourceId = randomResolver.resourceId - def postedJsonBody = mapper.writeValueAsString(randomResolver) - - 1 * repository.findByResourceId(randomResourceId) >> null - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/FileBackedHttp') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isNotFound()) - } -} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy deleted file mode 100644 index 14c925c5c..000000000 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy +++ /dev/null @@ -1,276 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller - -import com.fasterxml.jackson.databind.ObjectMapper -import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration -import edu.internet2.tier.shibboleth.admin.ui.repository.LocalDynamicMetadataResolverRepository -import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator -import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator -import edu.internet2.tier.shibboleth.admin.util.AttributeUtility -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest -import org.springframework.data.jpa.repository.config.EnableJpaRepositories -import org.springframework.test.context.ContextConfiguration -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import spock.lang.Specification - -import static org.hamcrest.CoreMatchers.containsString -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.* - -/** - * @author Bill Smith (wsmith@unicon.net) - */ -@DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) -@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) -@EntityScan("edu.internet2.tier.shibboleth.admin.ui") -class LocalDynamicMetadataProviderControllerTests extends Specification { - RandomGenerator randomGenerator - TestObjectGenerator testObjectGenerator - ObjectMapper mapper - - def repository = Mock(LocalDynamicMetadataResolverRepository) - def controller - def mockMvc - - @Autowired - AttributeUtility attributeUtility - - def setup() { - randomGenerator = new RandomGenerator() - testObjectGenerator = new TestObjectGenerator(attributeUtility) - mapper = new ObjectMapper() - - controller = new LocalDynamicMetadataProviderController ( - repository: repository - ) - - mockMvc = MockMvcBuilders.standaloneSetup(controller).build() - } - - def "DELETE deletes the desired resolver"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> true - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/LocalDynamic/$randomResourceId")) - - then: - result.andExpect(status().isAccepted()) - } - - def "DELETE returns error when desired resolver is not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.deleteByResourceId(randomResourceId) >> false - - when: - def result = mockMvc.perform( - delete("/api/MetadataProvider/LocalDynamic/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "POST a new resolver properly persists and returns the new persisted resolver"() { - given: - def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - resolver.version = resolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolver.getName()) >> null - 1 * repository.save(_) >> resolver - - def expectedResolverUUID = resolver.getResourceId() - def expectedResponseHeader = 'Location' - def expectedResponseHeaderValue = "/api/MetadataProvider/LocalDynamic/$expectedResolverUUID" - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isCreated()) - .andExpect(content().json(postedJsonBody, false)) - .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) - } - - def "POST a new resolver that has a name of a persisted resolver returns conflict"() { - given: - def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - resolver.version = resolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolver.name) >> resolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - post('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "GET by resourceId returns the desired persisted resolver"() { - given: - def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - resolver.version = resolver.hashCode() - def resolverJson = mapper.writeValueAsString(resolver) - def resolverId = resolver.resourceId - - 1 * repository.findByResourceId(resolverId) >> resolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/LocalDynamic/$resolverId")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resource id returns not found"() { - given: - def randomResourceId = randomGenerator.randomId() - - 1 * repository.findByResourceId(randomResourceId) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/LocalDynamic/$randomResourceId")) - - then: - result.andExpect(status().isNotFound()) - } - - def "GET by resolver name returns the desired persisted resolver"() { - given: - def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - resolver.version = resolver.hashCode() - def resolverName = resolver.name - def resolverJson = mapper.writeValueAsString(resolver) - - 1 * repository.findByName(resolverName) >> resolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/LocalDynamic/name/$resolverName")) - - then: - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(resolverJson, false)) - } - - def "GET by unknown resolver name returns not found"() { - given: - def randomResolverName = randomGenerator.randomString(10) - - 1 * repository.findByName(randomResolverName) >> null - - when: - def result = mockMvc.perform( - get("/api/MetadataProvider/LocalDynamic/name/$randomResolverName")) - - then: - result.andExpect(status().isNotFound()) - } - - def "PUT allows for a successful update of an already-persisted resolver"() { - given: - def existingResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def resourceId = existingResolver.resourceId - def updatedResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - updatedResolver.setResourceId(resourceId) - updatedResolver.setVersion(existingResolver.hashCode()) - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(resourceId) >> existingResolver - 1 * repository.save(_) >> updatedResolver - - def expectedResponseContentType = APPLICATION_JSON_UTF8 - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - def expectedJson = new JsonSlurper().parseText(postedJsonBody) - expectedJson << [version: updatedResolver.hashCode()] - result.andExpect(status().isOk()) - .andExpect(content().contentType(expectedResponseContentType)) - .andExpect(content().json(JsonOutput.toJson(expectedJson), false)) - } - - def "PUT of an updated resolver with an incorrect version returns a conflict"() { - given: - def existingResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def resourceId = existingResolver.resourceId - - def updatedResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - updatedResolver.resourceId = resourceId - updatedResolver.version = updatedResolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(resourceId) >> existingResolver - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isConflict()) - } - - def "PUT of a resolver that is not persisted returns not found"() { - given: - def existingResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - existingResolver.version = existingResolver.hashCode() - def resourceId = existingResolver.resourceId - - def updatedResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() - updatedResolver.resourceId = resourceId - updatedResolver.version = updatedResolver.hashCode() - def postedJsonBody = mapper.writeValueAsString(updatedResolver) - - 1 * repository.findByResourceId(resourceId) >> null - 0 * repository.save(_) - - when: - def result = mockMvc.perform( - put('/api/MetadataProvider/LocalDynamic') - .contentType(APPLICATION_JSON_UTF8) - .content(postedJsonBody)) - - then: - result.andExpect(status().isNotFound()) - } -} From 7945464480e9ae59087aab73acb151710767e9b6 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 19 Jul 2018 17:40:57 +0000 Subject: [PATCH 12/20] Merged in feature/SHIBUI-625 (pull request #119) SHIBUI-625 Provider Editor * SHIBUI-625 Initial stub of editor * SHIBUI-625 Implemented pages for provider editing * SHIBUI-625 Implemented editor navigation * SHIBUI-625 Implemented editor for metadata providers * Fixed tests, consolidated validation for provider names * SHIBUI-625 Added unit tests Approved-by: Shibui Jenkins Approved-by: Ryan Mathis --- .../model/providers/base-metadata-provider.ts | 5 + .../file-backed-http-metadata-provider.ts | 8 +- .../metadata/domain/model/providers/index.ts | 3 +- .../dashboard-providers-list.component.html | 5 +- .../provider/action/collection.action.ts | 31 ++- .../metadata/provider/action/entity.action.ts | 8 - .../provider-wizard-summary.component.spec.ts | 27 +- .../provider-wizard-summary.component.ts | 2 +- .../provider-edit-step.component.html | 8 + .../provider-edit-step.component.spec.ts | 90 +++++++ .../container/provider-edit-step.component.ts | 93 +++++++ .../container/provider-edit.component.html | 88 +++++++ .../container/provider-edit.component.spec.ts | 115 +++++++++ .../container/provider-edit.component.ts | 107 ++++++++ .../provider-filter-list.component.html | 5 + .../provider-filter-list.component.spec.ts | 56 ++++ .../provider-filter-list.component.ts | 14 + .../container/provider-select.component.html | 1 + .../provider-select.component.spec.ts | 56 ++++ .../container/provider-select.component.ts | 33 +++ .../provider-wizard-step.component.html | 14 +- .../provider-wizard-step.component.ts | 36 +-- .../container/provider-wizard.component.html | 42 +-- .../container/provider-wizard.component.ts | 7 +- .../container/provider.component.html | 15 +- .../provider/container/provider.component.ts | 8 +- .../provider/effect/collection.effect.ts | 60 ++++- .../metadata/provider/effect/entity.effect.ts | 29 +++ .../provider/model/base.provider.form.spec.ts | 142 ++++++++++ .../provider/model/base.provider.form.ts | 60 +++++ .../model/file-backed-http.provider.form.ts | 67 +++-- ui/src/app/metadata/provider/model/index.ts | 9 +- .../metadata/provider/model/provider.form.ts | 6 +- .../app/metadata/provider/provider.module.ts | 16 +- .../app/metadata/provider/provider.routing.ts | 27 +- .../provider/reducer/collection.reducer.ts | 7 + .../provider/reducer/entity.reducer.ts | 12 +- ui/src/app/wizard/model/wizard.ts | 4 +- ui/src/app/wizard/reducer/index.spec.ts | 1 - .../filebacked-http-advanced.schema.json | 6 + .../filebacked-http-common.editor.schema.json | 244 ++++++++++++++++++ ui/src/testing/activated-route.stub.ts | 13 +- 42 files changed, 1440 insertions(+), 140 deletions(-) create mode 100644 ui/src/app/metadata/domain/model/providers/base-metadata-provider.ts create mode 100644 ui/src/app/metadata/provider/container/provider-edit-step.component.html create mode 100644 ui/src/app/metadata/provider/container/provider-edit-step.component.spec.ts create mode 100644 ui/src/app/metadata/provider/container/provider-edit-step.component.ts create mode 100644 ui/src/app/metadata/provider/container/provider-edit.component.html create mode 100644 ui/src/app/metadata/provider/container/provider-edit.component.spec.ts create mode 100644 ui/src/app/metadata/provider/container/provider-edit.component.ts create mode 100644 ui/src/app/metadata/provider/container/provider-filter-list.component.html create mode 100644 ui/src/app/metadata/provider/container/provider-filter-list.component.spec.ts create mode 100644 ui/src/app/metadata/provider/container/provider-filter-list.component.ts create mode 100644 ui/src/app/metadata/provider/container/provider-select.component.html create mode 100644 ui/src/app/metadata/provider/container/provider-select.component.spec.ts create mode 100644 ui/src/app/metadata/provider/container/provider-select.component.ts create mode 100644 ui/src/app/metadata/provider/effect/entity.effect.ts create mode 100644 ui/src/app/metadata/provider/model/base.provider.form.spec.ts create mode 100644 ui/src/app/metadata/provider/model/base.provider.form.ts create mode 100644 ui/src/assets/schema/provider/filebacked-http-advanced.schema.json create mode 100644 ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json diff --git a/ui/src/app/metadata/domain/model/providers/base-metadata-provider.ts b/ui/src/app/metadata/domain/model/providers/base-metadata-provider.ts new file mode 100644 index 000000000..02d9ef01d --- /dev/null +++ b/ui/src/app/metadata/domain/model/providers/base-metadata-provider.ts @@ -0,0 +1,5 @@ +import { MetadataProvider } from '../metadata-provider'; + +export interface BaseMetadataProvider extends MetadataProvider { + metadataFilters: any[]; +} diff --git a/ui/src/app/metadata/domain/model/providers/file-backed-http-metadata-provider.ts b/ui/src/app/metadata/domain/model/providers/file-backed-http-metadata-provider.ts index 7d107036b..1d1e39291 100644 --- a/ui/src/app/metadata/domain/model/providers/file-backed-http-metadata-provider.ts +++ b/ui/src/app/metadata/domain/model/providers/file-backed-http-metadata-provider.ts @@ -1,5 +1,7 @@ -import { MetadataProvider } from '../metadata-provider'; +import { BaseMetadataProvider } from './base-metadata-provider'; -export interface FileBackedHttpMetadataProvider extends MetadataProvider { - metadataFilters: any[]; +export interface FileBackedHttpMetadataProvider extends BaseMetadataProvider { + id: string; + metadataURL: string; + reloadableMetadataResolverAttributes: any; } diff --git a/ui/src/app/metadata/domain/model/providers/index.ts b/ui/src/app/metadata/domain/model/providers/index.ts index 0cd02d47d..6bce32434 100644 --- a/ui/src/app/metadata/domain/model/providers/index.ts +++ b/ui/src/app/metadata/domain/model/providers/index.ts @@ -1 +1,2 @@ -export * from './file-backed-http-metadata-provider'; \ No newline at end of file +export * from './file-backed-http-metadata-provider'; +export * from './base-metadata-provider'; diff --git a/ui/src/app/metadata/manager/container/dashboard-providers-list.component.html b/ui/src/app/metadata/manager/container/dashboard-providers-list.component.html index cf446c1d1..5690a336e 100644 --- a/ui/src/app/metadata/manager/container/dashboard-providers-list.component.html +++ b/ui/src/app/metadata/manager/container/dashboard-providers-list.component.html @@ -8,7 +8,10 @@
-
{{ providers$ | async | json }}
+ + {{ provider.name }} +
diff --git a/ui/src/app/metadata/provider/action/collection.action.ts b/ui/src/app/metadata/provider/action/collection.action.ts index 99e71c758..7eaf7d1df 100644 --- a/ui/src/app/metadata/provider/action/collection.action.ts +++ b/ui/src/app/metadata/provider/action/collection.action.ts @@ -7,9 +7,13 @@ export enum ProviderCollectionActionTypes { UPDATE_PROVIDER_SUCCESS = '[Metadata Provider] Update Success', UPDATE_PROVIDER_FAIL = '[Metadata Provider] Update Fail', - LOAD_PROVIDER_REQUEST = '[Metadata Provider Collection] Provider REQUEST', - LOAD_PROVIDER_SUCCESS = '[Metadata Provider Collection] Provider SUCCESS', - LOAD_PROVIDER_ERROR = '[Metadata Provider Collection] Provider ERROR', + LOAD_PROVIDER_REQUEST = '[Metadata Provider Collection] Provider Load REQUEST', + LOAD_PROVIDER_SUCCESS = '[Metadata Provider Collection] Provider Load SUCCESS', + LOAD_PROVIDER_ERROR = '[Metadata Provider Collection] Provider Load ERROR', + + SELECT_PROVIDER_REQUEST = '[Metadata Provider Collection] Provider SELECT REQUEST', + SELECT_PROVIDER_SUCCESS = '[Metadata Provider Collection] Provider SELECT SUCCESS', + SELECT_PROVIDER_ERROR = '[Metadata Provider Collection] Provider SELECT ERROR', ADD_PROVIDER_REQUEST = '[Metadata Provider Collection] Add Provider', ADD_PROVIDER_SUCCESS = '[Metadata Provider Collection] Add Provider Success', @@ -38,6 +42,24 @@ export class LoadProviderError implements Action { constructor(public payload: any) { } } +export class SelectProviderRequest implements Action { + readonly type = ProviderCollectionActionTypes.SELECT_PROVIDER_REQUEST; + + constructor(public payload: any) { } +} + +export class SelectProviderSuccess implements Action { + readonly type = ProviderCollectionActionTypes.SELECT_PROVIDER_SUCCESS; + + constructor(public payload: Update) { } +} + +export class SelectProviderError implements Action { + readonly type = ProviderCollectionActionTypes.SELECT_PROVIDER_ERROR; + + constructor(public payload: any) { } +} + export class UpdateProviderRequest implements Action { readonly type = ProviderCollectionActionTypes.UPDATE_PROVIDER_REQUEST; @@ -96,6 +118,9 @@ export type ProviderCollectionActionsUnion = | LoadProviderRequest | LoadProviderSuccess | LoadProviderError + | SelectProviderRequest + | SelectProviderSuccess + | SelectProviderError | AddProviderRequest | AddProviderSuccess | AddProviderFail diff --git a/ui/src/app/metadata/provider/action/entity.action.ts b/ui/src/app/metadata/provider/action/entity.action.ts index 84f7dbd4a..44c2ca90d 100644 --- a/ui/src/app/metadata/provider/action/entity.action.ts +++ b/ui/src/app/metadata/provider/action/entity.action.ts @@ -2,18 +2,11 @@ import { Action } from '@ngrx/store'; import { MetadataProvider } from '../../domain/model'; export enum EntityActionTypes { - SELECT_PROVIDER = '[Provider Entity] Select Provider', UPDATE_PROVIDER = '[Provider Entity] Update Provider', CLEAR_PROVIDER = '[Provider Entity] Clear', RESET_CHANGES = '[Provider Entity] Reset Changes' } -export class SelectProvider implements Action { - readonly type = EntityActionTypes.SELECT_PROVIDER; - - constructor(public payload: MetadataProvider) { } -} - export class UpdateProvider implements Action { readonly type = EntityActionTypes.UPDATE_PROVIDER; @@ -29,7 +22,6 @@ export class ResetChanges implements Action { } export type EntityActionUnion = - | SelectProvider | UpdateProvider | ClearProvider | ResetChanges; diff --git a/ui/src/app/metadata/provider/component/provider-wizard-summary.component.spec.ts b/ui/src/app/metadata/provider/component/provider-wizard-summary.component.spec.ts index d951464ad..5c13cc74a 100644 --- a/ui/src/app/metadata/provider/component/provider-wizard-summary.component.spec.ts +++ b/ui/src/app/metadata/provider/component/provider-wizard-summary.component.spec.ts @@ -5,7 +5,7 @@ import { StoreModule, Store, combineReducers } from '@ngrx/store'; import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; -import { ProviderWizardSummaryComponent } from './provider-wizard-summary.component'; +import { ProviderWizardSummaryComponent, getStepProperties } from './provider-wizard-summary.component'; import * as fromRoot from '../reducer'; import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from 'ngx-schema-form'; import * as fromWizard from '../../../wizard/reducer'; @@ -76,6 +76,31 @@ describe('Provider Wizard Summary Component', () => { expect(app).toBeTruthy(); })); + describe('getStepProperties function', () => { + it('should return an empty array of schema or schema.properties is not defined', () => { + expect(getStepProperties(null, {})).toEqual([]); + expect(getStepProperties({}, {})).toEqual([]); + }); + + it('should return a formatted list of properties', () => { + expect(getStepProperties(SCHEMA, {}).length).toBe(2); + }); + }); + + describe('gotoPage function', () => { + it('should emit an empty string if page is null', () => { + spyOn(app.onPageSelect, 'emit'); + app.gotoPage(); + expect(app.onPageSelect.emit).toHaveBeenCalledWith(''); + }); + + it('should emit the provided page', () => { + spyOn(app.onPageSelect, 'emit'); + app.gotoPage('foo'); + expect(app.onPageSelect.emit).toHaveBeenCalledWith('foo'); + }); + }); + describe('ngOnChanges', () => { it('should set columns and sections if summary is provided', () => { instance.summary = { diff --git a/ui/src/app/metadata/provider/component/provider-wizard-summary.component.ts b/ui/src/app/metadata/provider/component/provider-wizard-summary.component.ts index 822e51337..c74c4935b 100644 --- a/ui/src/app/metadata/provider/component/provider-wizard-summary.component.ts +++ b/ui/src/app/metadata/provider/component/provider-wizard-summary.component.ts @@ -13,7 +13,7 @@ interface Section { properties: Property[]; } -function getStepProperties(schema: any, model: any): Property[] { +export function getStepProperties(schema: any, model: any): Property[] { if (!schema || !schema.properties) { return []; } return Object.keys(schema.properties).map(property => ({ name: schema.properties[property].title, diff --git a/ui/src/app/metadata/provider/container/provider-edit-step.component.html b/ui/src/app/metadata/provider/container/provider-edit-step.component.html new file mode 100644 index 000000000..5d07730fd --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-edit-step.component.html @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/ui/src/app/metadata/provider/container/provider-edit-step.component.spec.ts b/ui/src/app/metadata/provider/container/provider-edit-step.component.spec.ts new file mode 100644 index 000000000..012f1f1c0 --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-edit-step.component.spec.ts @@ -0,0 +1,90 @@ +import { Component, ViewChild } from '@angular/core'; +import { TestBed, async, ComponentFixture, fakeAsync, tick } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { StoreModule, Store, combineReducers } from '@ngrx/store'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; +import { ProviderEditStepComponent } from './provider-edit-step.component'; +import * as fromRoot from '../reducer'; +import * as fromWizard from '../../../wizard/reducer'; +import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from 'ngx-schema-form'; +import { SharedModule } from '../../../shared/shared.module'; +import { SetDefinition } from '../../../wizard/action/wizard.action'; +import { FileBackedHttpMetadataProviderEditor } from '../model'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(ProviderEditStepComponent) + public componentUnderTest: ProviderEditStepComponent; +} + +describe('Provider Edit Step Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: ProviderEditStepComponent; + let store: Store; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule.forRoot(), + RouterTestingModule, + SchemaFormModule.forRoot(), + SharedModule, + StoreModule.forRoot({ + provider: combineReducers(fromRoot.reducers), + wizard: combineReducers(fromWizard.reducers, { + wizard: { + index: 'common', + disabled: false, + definition: FileBackedHttpMetadataProviderEditor, + schemaCollection: [] + } + }) + }) + ], + declarations: [ + ProviderEditStepComponent, + TestHostComponent + ], + providers: [ + { provide: WidgetRegistry, useClass: DefaultWidgetRegistry } + ] + }).compileComponents(); + + store = TestBed.get(Store); + spyOn(store, 'dispatch'); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should instantiate the component', async(() => { + expect(app).toBeTruthy(); + })); + + describe('updateStatus method', () => { + it('should update the status with provided errors', () => { + app.currentPage = 'common'; + app.updateStatus({value: 'common'}); + app.updateStatus({value: 'foo'}); + expect(store.dispatch).toHaveBeenCalledTimes(2); + }); + }); + + describe('valueChangeEmitted$ subject', () => { + it('should update the provider', fakeAsync(() => { + app.valueChangeSubject.next({value: { name: 'foo' } }); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(store.dispatch).toHaveBeenCalled(); + })); + }); +}); diff --git a/ui/src/app/metadata/provider/container/provider-edit-step.component.ts b/ui/src/app/metadata/provider/container/provider-edit-step.component.ts new file mode 100644 index 000000000..63256e8a6 --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-edit-step.component.ts @@ -0,0 +1,93 @@ +import { Component, OnDestroy } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { Store } from '@ngrx/store'; + +import * as fromProvider from '../reducer'; +import { UpdateStatus } from '../action/editor.action'; +import { Wizard } from '../../../wizard/model'; +import { MetadataProvider } from '../../domain/model'; + +import * as fromWizard from '../../../wizard/reducer'; +import { withLatestFrom, map, skipWhile, distinctUntilChanged } from 'rxjs/operators'; +import { UpdateProvider } from '../action/entity.action'; + +@Component({ + selector: 'provider-edit-step', + templateUrl: './provider-edit-step.component.html', + styleUrls: [] +}) + +export class ProviderEditStepComponent implements OnDestroy { + valueChangeSubject = new Subject>(); + private valueChangeEmitted$ = this.valueChangeSubject.asObservable(); + + statusChangeSubject = new Subject>(); + private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); + + currentPage: string; + + namesList: string[] = []; + + schema$: Observable; + provider$: Observable; + model$: Observable; + definition$: Observable>; + changes$: Observable; + + validators$: Observable<{ [key: string]: any }>; + + constructor( + private store: Store + ) { + this.schema$ = this.store.select(fromProvider.getSchema); + this.definition$ = this.store.select(fromWizard.getWizardDefinition); + this.changes$ = this.store.select(fromProvider.getEntityChanges); + this.provider$ = this.store.select(fromProvider.getSelectedProvider); + + this.validators$ = this.store.select(fromProvider.getProviderNames).pipe( + withLatestFrom(this.definition$, this.provider$), + map(([names, def, provider]) => def.getValidators(names.filter(n => n !== provider.name))) + ); + + this.model$ = this.schema$.pipe( + withLatestFrom( + this.store.select(fromProvider.getSelectedProvider), + this.store.select(fromWizard.getModel), + this.changes$, + this.definition$ + ), + map(([schema, provider, model, changes, definition]) => ({ + model: { + ...model, + ...provider, + ...changes + }, + definition + })), + skipWhile(({ model, definition }) => !definition || !model), + map(({ model, definition }) => definition.translate.formatter(model)) + ); + + this.valueChangeEmitted$.pipe( + map(changes => changes.value), + withLatestFrom(this.definition$), + skipWhile(([ changes, definition ]) => !definition || !changes), + map(([ changes, definition ]) => definition.translate.parser(changes)) + ) + .subscribe(changes => this.store.dispatch(new UpdateProvider(changes))); + + this.statusChangeEmitted$.pipe(distinctUntilChanged()).subscribe(errors => this.updateStatus(errors)); + + this.store.select(fromWizard.getWizardIndex).subscribe(i => this.currentPage = i); + } + + updateStatus(errors: any): void { + const status = { [this.currentPage]: !(errors.value) ? 'VALID' : 'INVALID' }; + this.store.dispatch(new UpdateStatus(status)); + } + + ngOnDestroy() { + this.valueChangeSubject.complete(); + } +} + diff --git a/ui/src/app/metadata/provider/container/provider-edit.component.html b/ui/src/app/metadata/provider/container/provider-edit.component.html new file mode 100644 index 000000000..3778fe440 --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-edit.component.html @@ -0,0 +1,88 @@ +
+
+
+
+ + Edit Metadata Provider - {{ (provider$ | async).name }} +
+
+
+
+
+
+ +
+
+ +   + +
+
+
+ + All forms must be valid before changes can be saved! +
+
+
+
+
+
+ +
{{ status$ | async | json }}
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/ui/src/app/metadata/provider/container/provider-edit.component.spec.ts b/ui/src/app/metadata/provider/container/provider-edit.component.spec.ts new file mode 100644 index 000000000..096a269c7 --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-edit.component.spec.ts @@ -0,0 +1,115 @@ +import { Component, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { APP_BASE_HREF } from '@angular/common'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { StoreModule, Store, combineReducers } from '@ngrx/store'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; +import { ProviderEditComponent } from './provider-edit.component'; +import * as fromRoot from '../reducer'; +import * as fromWizard from '../../../wizard/reducer'; +import { SharedModule } from '../../../shared/shared.module'; +import { ActivatedRouteStub } from '../../../../testing/activated-route.stub'; +import { FileBackedHttpMetadataProviderEditor } from '../model'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(ProviderEditComponent) + public componentUnderTest: ProviderEditComponent; +} + +describe('Provider Edit Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: ProviderEditComponent; + let store: Store; + let router: Router; + let activatedRoute: ActivatedRouteStub = new ActivatedRouteStub(); + let child: ActivatedRouteStub = new ActivatedRouteStub(); + child.testParamMap = { form: 'common' }; + activatedRoute.firstChild = child; + + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule.forRoot(), + RouterTestingModule, + SharedModule, + StoreModule.forRoot({ + provider: combineReducers(fromRoot.reducers), + wizard: combineReducers(fromWizard.reducers, { + wizard: { + index: 'common', + disabled: false, + definition: FileBackedHttpMetadataProviderEditor, + schemaCollection: [] + } + }) + }) + ], + declarations: [ + ProviderEditComponent, + TestHostComponent + ], + providers: [ + { provide: ActivatedRoute, useValue: activatedRoute }, + { provide: APP_BASE_HREF, useValue: '/' } + ] + }).compileComponents(); + + store = TestBed.get(Store); + router = TestBed.get(Router); + spyOn(store, 'dispatch'); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should instantiate the component', async(() => { + expect(app).toBeTruthy(); + })); + + describe('setIndex method', () => { + it('should interrupt event default and dispatch an event', () => { + const ev = { + preventDefault: jasmine.createSpy('preventDefault'), + stopPropagation: jasmine.createSpy('stopPropagation') + }; + app.setIndex(ev, 'common'); + expect(ev.preventDefault).toHaveBeenCalled(); + expect(ev.stopPropagation).toHaveBeenCalled(); + expect(store.dispatch).toHaveBeenCalled(); + }); + }); + + describe('go method', () => { + it('should route to the given child form', () => { + spyOn(router, 'navigate'); + app.go('common'); + expect(router.navigate).toHaveBeenCalled(); + }); + }); + + describe('save method', () => { + it('should route to the given child form', () => { + app.save(); + expect(store.dispatch).toHaveBeenCalled(); + }); + }); + + describe('cancel method', () => { + it('should route to the metadata manager', () => { + spyOn(router, 'navigate'); + app.cancel(); + expect(router.navigate).toHaveBeenCalled(); + }); + }); +}); diff --git a/ui/src/app/metadata/provider/container/provider-edit.component.ts b/ui/src/app/metadata/provider/container/provider-edit.component.ts new file mode 100644 index 000000000..79f5cb2a6 --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-edit.component.ts @@ -0,0 +1,107 @@ +import { Component, OnDestroy } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { skipWhile, map, combineLatest } from 'rxjs/operators'; +import { Store } from '@ngrx/store'; +import * as fromWizard from '../../../wizard/reducer'; +import * as fromProvider from '../reducer'; +import { ClearWizard, SetDefinition, SetIndex } from '../../../wizard/action/wizard.action'; +import { ClearEditor, LoadSchemaRequest } from '../action/editor.action'; +import { MetadataProvider } from '../../domain/model'; +import { ClearProvider } from '../action/entity.action'; +import { MetadataProviderEditorTypes } from '../model'; +import { Wizard, WizardStep } from '../../../wizard/model'; +import { UpdateProviderRequest } from '../action/collection.action'; + +@Component({ + selector: 'provider-edit', + templateUrl: './provider-edit.component.html', + styleUrls: [] +}) + +export class ProviderEditComponent implements OnDestroy { + + provider$: Observable; + definition$: Observable>; + index$: Observable; + invalidForms$: Observable; + currentPage$: Observable; + + valid$: Observable; + isInvalid$: Observable; + status$: Observable; + + latest: MetadataProvider; + + constructor( + private store: Store, + private router: Router, + private route: ActivatedRoute + ) { + this.provider$ = this.store.select(fromProvider.getSelectedProvider).pipe(skipWhile(d => !d)); + this.definition$ = this.store.select(fromWizard.getWizardDefinition).pipe(skipWhile(d => !d)); + this.index$ = this.store.select(fromWizard.getWizardIndex).pipe(skipWhile(i => !i)); + this.valid$ = this.store.select(fromProvider.getEditorIsValid); + this.isInvalid$ = this.valid$.pipe(map(v => !v)); + this.status$ = this.store.select(fromProvider.getInvalidEditorForms); + + let startIndex$ = this.route.firstChild ? + this.route.firstChild.params.pipe(map(p => p.form || 'filter-list')) : + this.definition$.pipe(map(d => d.steps[0].id)); + + startIndex$ + .subscribe(index => { + this.store.dispatch(new SetIndex(index)); + }); + + this.provider$ + .subscribe(provider => { + this.store.dispatch(new SetDefinition({ + ...MetadataProviderEditorTypes.find(def => def.type === provider['@type']) + })); + }); + + this.index$.subscribe(id => this.go(id)); + + this.store + .select(fromWizard.getCurrentWizardSchema) + .pipe(skipWhile(s => !s)) + .subscribe(s => { + if (s) { + this.store.dispatch(new LoadSchemaRequest(s)); + } + }); + + this.store.select(fromProvider.getEntityChanges).subscribe(changes => this.latest = changes); + + this.invalidForms$ = this.store.select(fromProvider.getInvalidEditorForms); + this.currentPage$ = this.index$.pipe( + combineLatest(this.definition$, (index, definition) => (definition.steps.find(r => r.id === index))) + ); + } + + go(id: string): void { + this.router.navigate(['./', id], { relativeTo: this.route }); + } + + setIndex($event: Event, id: string): void { + $event.preventDefault(); + $event.stopPropagation(); + this.store.dispatch(new SetIndex(id)); + } + + ngOnDestroy() { + this.store.dispatch(new ClearProvider()); + this.store.dispatch(new ClearWizard()); + this.store.dispatch(new ClearEditor()); + } + + save(): void { + this.store.dispatch(new UpdateProviderRequest(this.latest)); + } + + cancel(): void { + this.router.navigate(['metadata', 'manager', 'providers']); + } +} + diff --git a/ui/src/app/metadata/provider/container/provider-filter-list.component.html b/ui/src/app/metadata/provider/container/provider-filter-list.component.html new file mode 100644 index 000000000..a1c8e0b1d --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-filter-list.component.html @@ -0,0 +1,5 @@ +
+
+ Filter list. +
+
\ No newline at end of file diff --git a/ui/src/app/metadata/provider/container/provider-filter-list.component.spec.ts b/ui/src/app/metadata/provider/container/provider-filter-list.component.spec.ts new file mode 100644 index 000000000..dd8c2ef95 --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-filter-list.component.spec.ts @@ -0,0 +1,56 @@ +import { Component, ViewChild } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { StoreModule, Store, combineReducers } from '@ngrx/store'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; +import { ProviderFilterListComponent } from './provider-filter-list.component'; +import * as fromRoot from '../reducer'; +import * as fromWizard from '../../../wizard/reducer'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(ProviderFilterListComponent) + public componentUnderTest: ProviderFilterListComponent; +} + +describe('Provider Filter List Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: ProviderFilterListComponent; + let store: Store; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule.forRoot(), + RouterTestingModule, + StoreModule.forRoot({ + provider: combineReducers(fromRoot.reducers), + wizard: combineReducers(fromWizard.reducers) + }) + ], + declarations: [ + ProviderFilterListComponent, + TestHostComponent + ], + providers: [] + }).compileComponents(); + + store = TestBed.get(Store); + spyOn(store, 'dispatch'); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should instantiate the component', async(() => { + expect(app).toBeTruthy(); + })); +}); diff --git a/ui/src/app/metadata/provider/container/provider-filter-list.component.ts b/ui/src/app/metadata/provider/container/provider-filter-list.component.ts new file mode 100644 index 000000000..a35bb9fc3 --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-filter-list.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { Store } from '@ngrx/store'; +import * as fromProvider from '../reducer'; + +@Component({ + selector: 'provider-filter-list', + templateUrl: './provider-filter-list.component.html', + styleUrls: [] +}) +export class ProviderFilterListComponent { + constructor( + private store: Store + ) { } +} diff --git a/ui/src/app/metadata/provider/container/provider-select.component.html b/ui/src/app/metadata/provider/container/provider-select.component.html new file mode 100644 index 000000000..90c6b6463 --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-select.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/app/metadata/provider/container/provider-select.component.spec.ts b/ui/src/app/metadata/provider/container/provider-select.component.spec.ts new file mode 100644 index 000000000..b8e07460b --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-select.component.spec.ts @@ -0,0 +1,56 @@ +import { Component, ViewChild } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { StoreModule, Store, combineReducers } from '@ngrx/store'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; +import { ProviderSelectComponent } from './provider-select.component'; +import * as fromRoot from '../reducer'; +import * as fromWizard from '../../../wizard/reducer'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(ProviderSelectComponent) + public componentUnderTest: ProviderSelectComponent; +} + +describe('Provider Select Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: ProviderSelectComponent; + let store: Store; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule.forRoot(), + RouterTestingModule, + StoreModule.forRoot({ + provider: combineReducers(fromRoot.reducers), + wizard: combineReducers(fromWizard.reducers) + }) + ], + declarations: [ + ProviderSelectComponent, + TestHostComponent + ], + providers: [] + }).compileComponents(); + + store = TestBed.get(Store); + spyOn(store, 'dispatch'); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should instantiate the component', async(() => { + expect(app).toBeTruthy(); + })); +}); diff --git a/ui/src/app/metadata/provider/container/provider-select.component.ts b/ui/src/app/metadata/provider/container/provider-select.component.ts new file mode 100644 index 000000000..dc0bcaa6b --- /dev/null +++ b/ui/src/app/metadata/provider/container/provider-select.component.ts @@ -0,0 +1,33 @@ +import { Component, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { Store } from '@ngrx/store'; + +import { ActivatedRoute } from '@angular/router'; +import { map, distinctUntilChanged } from 'rxjs/operators'; +import { SelectProviderRequest } from '../action/collection.action'; +import * as fromProviders from '../reducer'; + +@Component({ + selector: 'provider-select', + templateUrl: './provider-select.component.html', + styleUrls: [] +}) + +export class ProviderSelectComponent implements OnDestroy { + actionsSubscription: Subscription; + + constructor( + store: Store, + route: ActivatedRoute + ) { + this.actionsSubscription = route.params.pipe( + distinctUntilChanged(), + map(params => new SelectProviderRequest(params.providerId)) + ).subscribe(store); + } + + ngOnDestroy() { + this.actionsSubscription.unsubscribe(); + } +} + diff --git a/ui/src/app/metadata/provider/container/provider-wizard-step.component.html b/ui/src/app/metadata/provider/container/provider-wizard-step.component.html index 831e71fbb..5d07730fd 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard-step.component.html +++ b/ui/src/app/metadata/provider/container/provider-wizard-step.component.html @@ -1,6 +1,8 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts b/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts index 7d09b8ec1..dce4187d7 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts +++ b/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts @@ -10,7 +10,7 @@ import { SetDefinition } from '../../../wizard/action/wizard.action'; import { UpdateStatus } from '../action/editor.action'; import { Wizard } from '../../../wizard/model'; import { MetadataProvider } from '../../domain/model'; -import { MetadataProviderTypes, MetadataProviderWizard } from '../model'; +import { MetadataProviderWizardTypes, MetadataProviderWizard } from '../model'; import { UpdateProvider } from '../action/entity.action'; import { pick } from '../../../shared/util'; @@ -37,32 +37,7 @@ export class ProviderWizardStepComponent implements OnDestroy { namesList: string[] = []; - validators = { - '/': (value, property, form_current) => { - let errors; - // iterate all customer - Object.keys(value).forEach((key) => { - const item = value[key]; - const validatorKey = `/${key}`; - const validator = this.validators.hasOwnProperty(validatorKey) ? this.validators[validatorKey] : null; - const error = validator ? validator(item, { path: `/${key}` }, form_current) : null; - if (error) { - errors = errors || []; - errors.push(error); - } - }); - return errors; - }, - '/name': (value, property, form) => { - const err = this.namesList.indexOf(value) > -1 ? { - code: 'INVALID_NAME', - path: `#${property.path}`, - message: 'Name must be unique.', - params: [value] - } : null; - return err; - } - }; + validators$: Observable<{ [key: string]: any }>; constructor( private store: Store, @@ -71,7 +46,10 @@ export class ProviderWizardStepComponent implements OnDestroy { this.definition$ = this.store.select(fromWizard.getWizardDefinition); this.changes$ = this.store.select(fromProvider.getEntityChanges); - this.store.select(fromProvider.getProviderNames).subscribe(list => this.namesList = list); + this.validators$ = this.store.select(fromProvider.getProviderNames).pipe( + withLatestFrom(this.definition$), + map(([names, def]) => def.getValidators(names)) + ); this.model$ = this.schema$.pipe( withLatestFrom( @@ -106,7 +84,7 @@ export class ProviderWizardStepComponent implements OnDestroy { resetSelectedType(changes: any, schema: any, definition: any): { changes: any, definition: any } { const type = changes.value['@type']; if (type && type !== definition.type) { - const newDefinition = MetadataProviderTypes.find(def => def.type === type); + const newDefinition = MetadataProviderWizardTypes.find(def => def.type === type); if (newDefinition) { this.store.dispatch(new SetDefinition({ ...MetadataProviderWizard, diff --git a/ui/src/app/metadata/provider/container/provider-wizard.component.html b/ui/src/app/metadata/provider/container/provider-wizard.component.html index 196081a1f..7d1528b03 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard.component.html +++ b/ui/src/app/metadata/provider/container/provider-wizard.component.html @@ -1,17 +1,29 @@ -
- -
-
-
- +
+
+
+
+ Add a new metadata provider +
- - -
+
+
+ +
+
+
+ +
+
+ + +
+
+ + diff --git a/ui/src/app/metadata/provider/container/provider-wizard.component.ts b/ui/src/app/metadata/provider/container/provider-wizard.component.ts index ee18b72b0..4a599f33e 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard.component.ts +++ b/ui/src/app/metadata/provider/container/provider-wizard.component.ts @@ -4,7 +4,7 @@ import { Store } from '@ngrx/store'; import * as fromProvider from '../reducer'; import * as fromWizard from '../../../wizard/reducer'; -import { SetIndex, SetDisabled, ClearWizard } from '../../../wizard/action/wizard.action'; +import { SetIndex, SetDisabled, ClearWizard, SetDefinition } from '../../../wizard/action/wizard.action'; import { LoadSchemaRequest, ClearEditor } from '../action/editor.action'; import { startWith } from 'rxjs/operators'; import { Wizard, WizardStep } from '../../../wizard/model'; @@ -13,7 +13,7 @@ import { ClearProvider } from '../action/entity.action'; import { Router, ActivatedRoute } from '@angular/router'; import { map } from 'rxjs/operators'; import { AddProviderRequest } from '../action/collection.action'; - +import { MetadataProviderWizard } from '../model'; @Component({ selector: 'provider-wizard', @@ -67,6 +67,9 @@ export class ProviderWizardComponent implements OnDestroy { ); this.changes$.subscribe(c => this.provider = c); + + this.store.dispatch(new SetDefinition(MetadataProviderWizard)); + this.store.dispatch(new SetIndex(MetadataProviderWizard.steps[0].id)); } ngOnDestroy() { diff --git a/ui/src/app/metadata/provider/container/provider.component.html b/ui/src/app/metadata/provider/container/provider.component.html index 203223445..8d471248b 100644 --- a/ui/src/app/metadata/provider/container/provider.component.html +++ b/ui/src/app/metadata/provider/container/provider.component.html @@ -1,14 +1,3 @@ -
-
-
-
-
- Add a new metadata provider -
-
-
-
- -
-
+
+
\ No newline at end of file diff --git a/ui/src/app/metadata/provider/container/provider.component.ts b/ui/src/app/metadata/provider/container/provider.component.ts index afaf48a27..c23dc7066 100644 --- a/ui/src/app/metadata/provider/container/provider.component.ts +++ b/ui/src/app/metadata/provider/container/provider.component.ts @@ -1,9 +1,6 @@ import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; import * as fromProvider from '../reducer'; -import { SetDefinition, SetIndex } from '../../../wizard/action/wizard.action'; - -import { MetadataProviderWizard } from '../model'; @Component({ selector: 'provider-page', @@ -13,8 +10,5 @@ import { MetadataProviderWizard } from '../model'; export class ProviderComponent { constructor( private store: Store - ) { - this.store.dispatch(new SetDefinition(MetadataProviderWizard)); - this.store.dispatch(new SetIndex(MetadataProviderWizard.steps[0].id)); - } + ) {} } diff --git a/ui/src/app/metadata/provider/effect/collection.effect.ts b/ui/src/app/metadata/provider/effect/collection.effect.ts index 1a737839e..d4eb5ff0f 100644 --- a/ui/src/app/metadata/provider/effect/collection.effect.ts +++ b/ui/src/app/metadata/provider/effect/collection.effect.ts @@ -1,9 +1,10 @@ import { Injectable } from '@angular/core'; import { Effect, Actions, ofType } from '@ngrx/effects'; +import { Store } from '@ngrx/store'; import { Router } from '@angular/router'; import { of } from 'rxjs'; -import { map, catchError, switchMap, tap } from 'rxjs/operators'; +import { map, catchError, switchMap, tap, withLatestFrom } from 'rxjs/operators'; import { ProviderCollectionActionsUnion, ProviderCollectionActionTypes, @@ -12,9 +13,17 @@ import { AddProviderFail, LoadProviderRequest, LoadProviderSuccess, - LoadProviderError + LoadProviderError, + SelectProviderRequest, + SelectProviderSuccess, + SelectProviderError, + UpdateProviderRequest, + UpdateProviderSuccess, + UpdateProviderFail } from '../action/collection.action'; import { MetadataProviderService } from '../../domain/service/provider.service'; +import * as fromProvider from '../reducer'; + /* istanbul ignore next */ @Injectable() @@ -33,6 +42,20 @@ export class CollectionEffects { ) ); + @Effect() + selectProviders$ = this.actions$.pipe( + ofType(ProviderCollectionActionTypes.SELECT_PROVIDER_REQUEST), + map(action => action.payload), + switchMap(id => + this.providerService + .find(id) + .pipe( + map(provider => new SelectProviderSuccess({ id, changes: provider })), + catchError(error => of(new SelectProviderError(error))) + ) + ) + ); + @Effect() createProvider$ = this.actions$.pipe( ofType(ProviderCollectionActionTypes.ADD_PROVIDER_REQUEST), @@ -51,7 +74,37 @@ export class CollectionEffects { createProviderSuccessRedirect$ = this.actions$.pipe( ofType(ProviderCollectionActionTypes.ADD_PROVIDER_SUCCESS), map(action => action.payload), - tap(provider => this.router.navigate(['metadata'])) + tap(provider => this.router.navigate(['metadata', 'manager', 'providers'])) + ); + + @Effect() + updateProvider$ = this.actions$.pipe( + ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_REQUEST), + map(action => action.payload), + withLatestFrom(this.store.select(fromProvider.getSelectedProvider)), + map(([updates, original]) => ({ ...original, ...updates })), + switchMap(provider => + this.providerService + .update(provider) + .pipe( + map(p => new UpdateProviderSuccess({id: p.id, changes: p})), + catchError((e) => of(new UpdateProviderFail(e))) + ) + ) + ); + + @Effect({ dispatch: false }) + updateProviderSuccessReload$ = this.actions$.pipe( + ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_SUCCESS), + map(action => action.payload), + tap(provider => this.store.dispatch(new LoadProviderRequest())) + ); + + @Effect({ dispatch: false }) + updateProviderSuccessRedirect$ = this.actions$.pipe( + ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_SUCCESS), + map(action => action.payload), + tap(provider => this.router.navigate(['metadata', 'manager', 'providers'])) ); @Effect() @@ -64,6 +117,7 @@ export class CollectionEffects { constructor( private actions$: Actions, private router: Router, + private store: Store, private providerService: MetadataProviderService ) { } } /* istanbul ignore next */ diff --git a/ui/src/app/metadata/provider/effect/entity.effect.ts b/ui/src/app/metadata/provider/effect/entity.effect.ts new file mode 100644 index 000000000..36f5faa5a --- /dev/null +++ b/ui/src/app/metadata/provider/effect/entity.effect.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; +import { Effect, Actions, ofType } from '@ngrx/effects'; +import { Store } from '@ngrx/store'; +import { map, switchMap } from 'rxjs/operators'; +import { of } from 'rxjs'; + +import { SelectProviderSuccess, ProviderCollectionActionTypes } from '../action/collection.action'; + +import * as fromProvider from '../reducer'; +import { MetadataProvider } from '../../domain/model'; +import { UpdateProvider } from '../action/entity.action'; + +@Injectable() +export class EntityEffects { + + /* + @Effect() + loadModelSuccess$ = this.actions$.pipe( + ofType(ProviderCollectionActionTypes.SELECT_PROVIDER_SUCCESS), + map(action => action.payload.changes), + switchMap((provider: MetadataProvider) => of(new UpdateProvider(provider))) + ); + */ + + constructor( + private store: Store, + private actions$: Actions + ) { } +} diff --git a/ui/src/app/metadata/provider/model/base.provider.form.spec.ts b/ui/src/app/metadata/provider/model/base.provider.form.spec.ts new file mode 100644 index 000000000..307368ef2 --- /dev/null +++ b/ui/src/app/metadata/provider/model/base.provider.form.spec.ts @@ -0,0 +1,142 @@ +import { BaseMetadataProviderEditor } from './base.provider.form'; + +describe('BaseMetadataProviderForm', () => { + + const parser = BaseMetadataProviderEditor.translate.parser; + const formatter = BaseMetadataProviderEditor.translate.formatter; + + const requiredValidUntilFilter = { + maxValidityInterval: 1, + '@type': 'RequiredValidUntil' + }; + + const signatureValidationFilter = { + requireSignedRoot: true, + certificateFile: 'foo', + '@type': 'SignatureValidation' + }; + + const entityRoleWhiteListFilter = { + retainedRoles: ['foo', 'bar'], + removeRolelessEntityDescriptors: true, + removeEmptyEntitiesDescriptors: true, + '@type': 'EntityRoleWhiteList' + }; + + describe('parser', () => { + it('should transform the filters object to an array', () => { + let model = { + name: 'foo', + '@type': 'FileBackedHttpMetadataProvider', + enabled: true, + resourceId: 'foo', + metadataFilters: { + RequiredValidUntil: requiredValidUntilFilter, + SignatureValidation: signatureValidationFilter, + EntityRoleWhiteList: entityRoleWhiteListFilter + } + }; + expect( + parser(model) + ).toEqual( + { + ...model, + metadataFilters: [ + requiredValidUntilFilter, + signatureValidationFilter, + entityRoleWhiteListFilter + ] + } + ); + }); + + it('should return the object if metadataFilters is not provided', () => { + let model = { + name: 'foo', + '@type': 'FileBackedHttpMetadataProvider', + enabled: true, + resourceId: 'foo' + }; + expect( + parser(model) + ).toEqual( + model + ); + }); + }); + + describe('formatter', () => { + it('should transform the filters object to an array', () => { + let model = { + name: 'foo', + '@type': 'FileBackedHttpMetadataProvider', + enabled: true, + resourceId: 'foo', + metadataFilters: [ + requiredValidUntilFilter, + signatureValidationFilter, + entityRoleWhiteListFilter + ] + }; + expect( + formatter(model) + ).toEqual( + { + ...model, + metadataFilters: { + RequiredValidUntil: requiredValidUntilFilter, + SignatureValidation: signatureValidationFilter, + EntityRoleWhiteList: entityRoleWhiteListFilter + } + } + ); + }); + + it('should return the object if metadataFilters is not provided', () => { + let model = { + name: 'foo', + '@type': 'FileBackedHttpMetadataProvider', + enabled: true, + resourceId: 'foo' + }; + expect( + formatter(model) + ).toEqual( + model + ); + }); + }); + + describe('getValidators', () => { + it('should return a set of validator functions for the provider type', () => { + const validators = BaseMetadataProviderEditor.getValidators([]); + expect(validators).toBeDefined(); + expect(validators['/']).toBeDefined(); + expect(validators['/name']).toBeDefined(); + }); + + describe('name `/name` validator', () => { + const validators = BaseMetadataProviderEditor.getValidators(['foo', 'bar']); + + it('should return an invalid object when provided values are invalid based on name', () => { + expect(validators['/name']('foo', { path: '/name' })).toBeDefined(); + }); + + it('should return null when provided values are valid based on name', () => { + expect(validators['/name']('baz', { path: '/name' })).toBeNull(); + }); + }); + + describe('parent `/` validator', () => { + const validators = BaseMetadataProviderEditor.getValidators(['foo', 'bar']); + + it('should return a list of child errors', () => { + expect(validators['/']({name: 'foo'}, { path: '/name' }, {}).length).toBe(1); + }); + + it('should ignore properties that don\'t exist a list of child errors', () => { + expect(validators['/']({ foo: 'bar' }, { path: '/foo' }, {})).toBeUndefined(); + }); + }); + }); +}); diff --git a/ui/src/app/metadata/provider/model/base.provider.form.ts b/ui/src/app/metadata/provider/model/base.provider.form.ts new file mode 100644 index 000000000..a542ebb47 --- /dev/null +++ b/ui/src/app/metadata/provider/model/base.provider.form.ts @@ -0,0 +1,60 @@ +import { Wizard } from '../../../wizard/model'; +import { BaseMetadataProvider } from '../../domain/model/providers'; + +export const BaseMetadataProviderEditor: Wizard = { + label: 'BaseMetadataProvider', + type: 'BaseMetadataResolver', + getValidators(namesList: string[]): any { + const validators = { + '/': (value, property, form_current) => { + let errors; + // iterate all customer + Object.keys(value).forEach((key) => { + const item = value[key]; + const validatorKey = `/${key}`; + const validator = validators.hasOwnProperty(validatorKey) ? validators[validatorKey] : null; + const error = validator ? validator(item, { path: `/${key}` }, form_current) : null; + if (error) { + errors = errors || []; + errors.push(error); + } + }); + return errors; + }, + '/name': (value, property, form) => { + const err = namesList.indexOf(value) > -1 ? { + code: 'INVALID_NAME', + path: `#${property.path}`, + message: 'Name must be unique.', + params: [value] + } : null; + return err; + } + }; + return validators; + }, + translate: { + parser: (changes: any): BaseMetadataProvider => changes.metadataFilters ? ({ + ...changes, + metadataFilters: [ + ...Object.keys(changes.metadataFilters).reduce((collection, filterName) => ([ + ...collection, + { + ...changes.metadataFilters[filterName], + '@type': filterName + } + ]), []) + ] + }) : changes, + formatter: (changes: BaseMetadataProvider): any => changes.metadataFilters ? ({ + ...changes, + metadataFilters: { + ...(changes.metadataFilters || []).reduce((collection, filter) => ({ + ...collection, + [filter['@type']]: filter + }), {}) + } + }) : changes + }, + steps: [] +}; diff --git a/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts b/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts index 41723be8f..5af8642bb 100644 --- a/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts +++ b/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts @@ -1,32 +1,11 @@ import { Wizard } from '../../../wizard/model'; import { FileBackedHttpMetadataProvider } from '../../domain/model/providers/file-backed-http-metadata-provider'; +import { BaseMetadataProviderEditor } from './base.provider.form'; export const FileBackedHttpMetadataProviderWizard: Wizard = { + ...BaseMetadataProviderEditor, label: 'FileBackedHttpMetadataProvider', type: 'FileBackedHttpMetadataResolver', - translate: { - parser: (changes: any): FileBackedHttpMetadataProvider => changes.metadataFilters ? ({ - ...changes, - metadataFilters: [ - ...Object.keys(changes.metadataFilters).reduce((collection, filterName) => ([ - ...collection, - { - ...changes.metadataFilters[filterName], - '@type': filterName - } - ]), []) - ] - }) : changes, - formatter: (changes: FileBackedHttpMetadataProvider): any => changes.metadataFilters ? ({ - ...changes, - metadataFilters: { - ...(changes.metadataFilters || []).reduce((collection, filter) => ({ - ...collection, - [filter['@type']]: filter - }), {}) - } - }) : changes - }, steps: [ { id: 'common', @@ -60,3 +39,45 @@ export const FileBackedHttpMetadataProviderWizard: Wizard = { + ...FileBackedHttpMetadataProviderWizard, + steps: [ + { + id: 'filter-list', + label: 'Filter List', + index: 0 + }, + { + id: 'common', + label: 'Common Attributes', + index: 1, + initialValues: [], + schema: 'assets/schema/provider/filebacked-http-common.editor.schema.json' + }, + { + id: 'reloading', + label: 'Reloading Attributes', + index: 2, + initialValues: [], + schema: 'assets/schema/provider/filebacked-http-reloading.schema.json' + }, + { + id: 'filters', + label: 'Metadata Filter Plugins', + index: 3, + initialValues: [ + { key: 'metadataFilters', value: [] } + ], + schema: 'assets/schema/provider/filebacked-http-filters.schema.json' + }, + { + id: 'advanced', + label: 'Advanced Settings', + index: 4, + initialValues: [], + schema: 'assets/schema/provider/filebacked-http-advanced.schema.json' + } + ] +}; diff --git a/ui/src/app/metadata/provider/model/index.ts b/ui/src/app/metadata/provider/model/index.ts index 508650ca7..a6c438df4 100644 --- a/ui/src/app/metadata/provider/model/index.ts +++ b/ui/src/app/metadata/provider/model/index.ts @@ -1,8 +1,15 @@ import { FileBackedHttpMetadataProviderWizard } from './file-backed-http.provider.form'; +import { FileBackedHttpMetadataProviderEditor } from './file-backed-http.provider.form'; +import { BaseMetadataProviderEditor } from './base.provider.form'; -export const MetadataProviderTypes = [ +export const MetadataProviderWizardTypes = [ FileBackedHttpMetadataProviderWizard ]; +export const MetadataProviderEditorTypes = [ + FileBackedHttpMetadataProviderEditor, + BaseMetadataProviderEditor +]; + export * from './file-backed-http.provider.form'; export * from './provider.form'; diff --git a/ui/src/app/metadata/provider/model/provider.form.ts b/ui/src/app/metadata/provider/model/provider.form.ts index 698d20134..f7e0ae773 100644 --- a/ui/src/app/metadata/provider/model/provider.form.ts +++ b/ui/src/app/metadata/provider/model/provider.form.ts @@ -1,14 +1,12 @@ import { Wizard, WizardStep } from '../../../wizard/model'; import { MetadataProvider } from '../../domain/model'; import { Metadata } from '../../domain/domain.type'; +import { BaseMetadataProviderEditor } from './base.provider.form'; export const MetadataProviderWizard: Wizard = { + ...BaseMetadataProviderEditor, label: 'MetadataProvider', type: 'MetadataProvider', - translate: { - parser: changes => changes, - formatter: model => model - }, steps: [ { id: 'new', diff --git a/ui/src/app/metadata/provider/provider.module.ts b/ui/src/app/metadata/provider/provider.module.ts index 104bae13d..288adf30b 100644 --- a/ui/src/app/metadata/provider/provider.module.ts +++ b/ui/src/app/metadata/provider/provider.module.ts @@ -19,7 +19,12 @@ import { CustomWidgetRegistry } from '../../schema-form/registry'; import { SummaryPropertyComponent } from './component/summary-property.component'; import { CollectionEffects } from './effect/collection.effect'; import { SharedModule } from '../../shared/shared.module'; - +import { ProviderEditComponent } from './container/provider-edit.component'; +import { ProviderSelectComponent } from './container/provider-select.component'; +import { ProviderEditStepComponent } from './container/provider-edit-step.component'; +import { EntityEffects } from './effect/entity.effect'; +import { ProviderFilterListComponent } from './container/provider-filter-list.component'; +import { NgbDropdownModule } from '../../../../node_modules/@ng-bootstrap/ng-bootstrap'; @NgModule({ declarations: [ @@ -27,6 +32,10 @@ import { SharedModule } from '../../shared/shared.module'; ProviderWizardComponent, ProviderWizardStepComponent, ProviderWizardSummaryComponent, + ProviderEditComponent, + ProviderEditStepComponent, + ProviderSelectComponent, + ProviderFilterListComponent, SummaryPropertyComponent ], entryComponents: [], @@ -36,7 +45,8 @@ import { SharedModule } from '../../shared/shared.module'; WizardModule, RouterModule, SharedModule, - FormModule + FormModule, + NgbDropdownModule ], exports: [] }) @@ -55,7 +65,7 @@ export class ProviderModule { imports: [ ProviderModule, StoreModule.forFeature('provider', fromProvider.reducers), - EffectsModule.forFeature([EditorEffects, CollectionEffects]) + EffectsModule.forFeature([EntityEffects, EditorEffects, CollectionEffects]) ] }) export class RootProviderModule { } diff --git a/ui/src/app/metadata/provider/provider.routing.ts b/ui/src/app/metadata/provider/provider.routing.ts index 98cdddd85..cf491a92f 100644 --- a/ui/src/app/metadata/provider/provider.routing.ts +++ b/ui/src/app/metadata/provider/provider.routing.ts @@ -3,6 +3,10 @@ import { Routes } from '@angular/router'; import { ProviderComponent } from './container/provider.component'; import { ProviderWizardComponent } from './container/provider-wizard.component'; import { ProviderWizardStepComponent } from './container/provider-wizard-step.component'; +import { ProviderEditComponent } from './container/provider-edit.component'; +import { ProviderEditStepComponent } from './container/provider-edit-step.component'; +import { ProviderSelectComponent } from './container/provider-select.component'; +import { ProviderFilterListComponent } from './container/provider-filter-list.component'; export const ProviderRoutes: Routes = [ { @@ -11,8 +15,7 @@ export const ProviderRoutes: Routes = [ children: [ { path: 'wizard', - redirectTo: `wizard/new`, - pathMatch: 'prefix' + redirectTo: `wizard/new` }, { path: 'wizard', @@ -24,6 +27,26 @@ export const ProviderRoutes: Routes = [ component: ProviderWizardStepComponent } ] + }, + { + path: ':providerId', + component: ProviderSelectComponent, + children: [ + { + path: 'edit', + component: ProviderEditComponent, + children: [ + { + path: 'filter-list', + component: ProviderFilterListComponent + }, + { + path: ':form', + component: ProviderEditStepComponent + } + ] + } + ] } ] } diff --git a/ui/src/app/metadata/provider/reducer/collection.reducer.ts b/ui/src/app/metadata/provider/reducer/collection.reducer.ts index 6bc1af8e7..8cc0c6f0c 100644 --- a/ui/src/app/metadata/provider/reducer/collection.reducer.ts +++ b/ui/src/app/metadata/provider/reducer/collection.reducer.ts @@ -23,6 +23,13 @@ export const initialState: CollectionState = adapter.getInitialState({ export function reducer(state = initialState, action: ProviderCollectionActionsUnion): CollectionState { switch (action.type) { + case ProviderCollectionActionTypes.SELECT_PROVIDER_SUCCESS: { + return adapter.upsertOne(action.payload, { + ...state, + selectedProviderId: action.payload.id.toString() + }); + } + case ProviderCollectionActionTypes.LOAD_PROVIDER_SUCCESS: { return adapter.addAll(action.payload, { ...state, diff --git a/ui/src/app/metadata/provider/reducer/entity.reducer.ts b/ui/src/app/metadata/provider/reducer/entity.reducer.ts index 5aa7eceb1..d5b63eca1 100644 --- a/ui/src/app/metadata/provider/reducer/entity.reducer.ts +++ b/ui/src/app/metadata/provider/reducer/entity.reducer.ts @@ -3,13 +3,11 @@ import { EntityActionTypes, EntityActionUnion } from '../action/entity.action'; export interface EntityState { saving: boolean; - base: MetadataProvider; changes: MetadataProvider; } export const initialState: EntityState = { saving: false, - base: null, changes: null }; @@ -28,14 +26,6 @@ export function reducer(state = initialState, action: EntityActionUnion): Entity } }; } - case EntityActionTypes.SELECT_PROVIDER: { - return { - ...state, - base: { - ...action.payload - } - }; - } case EntityActionTypes.UPDATE_PROVIDER: { return { ...state, @@ -54,4 +44,4 @@ export function reducer(state = initialState, action: EntityActionUnion): Entity export const isEntitySaved = (state: EntityState) => !Object.keys(state.changes).length && !state.saving; export const getEntityChanges = (state: EntityState) => state.changes; export const isEditorSaving = (state: EntityState) => state.saving; -export const getUpdatedEntity = (state: EntityState) => ({ ...state.base, ...state.changes }); +export const getUpdatedEntity = (state: EntityState) => state.changes; diff --git a/ui/src/app/wizard/model/wizard.ts b/ui/src/app/wizard/model/wizard.ts index 8b5fe344e..3c9477f5a 100644 --- a/ui/src/app/wizard/model/wizard.ts +++ b/ui/src/app/wizard/model/wizard.ts @@ -6,13 +6,15 @@ export interface Wizard { parser(changes: Partial, schema?: any), formatter(changes: Partial, schema?: any) }; + + getValidators?(params: any): { [key: string]: any }; } export interface WizardStep { id: string; label: string; initialValues?: WizardValue[]; - schema: string; + schema?: string; index: number; } diff --git a/ui/src/app/wizard/reducer/index.spec.ts b/ui/src/app/wizard/reducer/index.spec.ts index 78ff09441..9c8b246f9 100644 --- a/ui/src/app/wizard/reducer/index.spec.ts +++ b/ui/src/app/wizard/reducer/index.spec.ts @@ -84,7 +84,6 @@ describe('wizard index selectors', () => { describe('getModelFn method', () => { it('should return the model', () => { const step = FileBackedHttpMetadataProviderWizard.steps.find(s => s.id === 'filters'); - console.log(step); expect(selectors.getModelFn(step)).toEqual({ metadataFilters: [] }); }); }); diff --git a/ui/src/assets/schema/provider/filebacked-http-advanced.schema.json b/ui/src/assets/schema/provider/filebacked-http-advanced.schema.json new file mode 100644 index 000000000..601aae3db --- /dev/null +++ b/ui/src/assets/schema/provider/filebacked-http-advanced.schema.json @@ -0,0 +1,6 @@ +{ + "type": "object", + "title": "Advanced Settings", + "order": [], + "properties": {} +} diff --git a/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json new file mode 100644 index 000000000..77590af65 --- /dev/null +++ b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json @@ -0,0 +1,244 @@ +{ + "type": "object", + "order": [ + "name", + "@type", + "id", + "metadataURL", + "initializeFromBackupFile", + "backingFile", + "backupFileInitNextRefreshDelay", + "requireValidMetadata", + "failFastInitialization", + "useDefaultPredicateRegistry", + "satisfyAnyPredicates" + ], + "required": [ + "id", + "metadataURL" + ], + "anyOf": [ + { + "properties": { + "initializeFromBackupFile": { + "enum": [ + true + ] + } + }, + "required": [ + "backingFile" + ] + }, + { + "properties": { + "initializeFromBackupFile": { + "enum": [ + false + ] + } + } + } + ], + "fieldsets": [ + { + "type": "section", + "fields": [ + "name", + "@type" + ] + }, + { + "fields": [ + "id", + "metadataURL", + "initializeFromBackupFile", + "backingFile", + "backupFileInitNextRefreshDelay", + "requireValidMetadata", + "failFastInitialization", + "useDefaultPredicateRegistry", + "satisfyAnyPredicates" + ] + } + ], + "properties": { + "name": { + "title": "Metadata Provider Name (Dashboard Display Only)", + "description": "Metadata Provider Name", + "type": "string", + "widget": { + "id": "string", + "help": "Must be unique." + } + }, + "@type": { + "title": "Metadata Provider Type", + "description": "Metadata Provider Type", + "placeholder": "Select a metadata provider type", + "type": "string", + "readOnly": true, + "widget": { + "id": "select" + }, + "oneOf": [ + { + "enum": [ + "FileBackedHttpMetadataResolver" + ], + "description": "FileBackedHttpMetadataProvider" + } + ] + }, + "id": { + "title": "ID", + "description": "Identifier for logging, identification for command line reload, etc.", + "type": "string", + "default": "", + "minLength": 1 + }, + "metadataURL": { + "title": "Metadata URL", + "description": "Identifier for logging, identification for command line reload, etc.", + "type": "string", + "default": "", + "minLength": 1 + }, + "initializeFromBackupFile": { + "title": "Initialize From Backup File?", + "description": "Flag indicating whether initialization should first attempt to load metadata from the backup file. If true, foreground initialization will be performed by loading the backing file, and then a refresh from the remote HTTP server will be scheduled to execute in a background thread, after a configured delay. This can improve IdP startup times when the remote HTTP file is large in size.", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "True" + }, + { + "enum": [ + false + ], + "description": "False" + } + ], + "default": true + }, + "backingFile": { + "title": "Backing File", + "description": "Specifies where the backing file is located. If the remote server is unavailable at startup, the backing file is loaded instead.", + "type": "string", + "default": "", + "visibleIf": { + "initializeFromBackupFile": [ + true + ] + } + }, + "backupFileInitNextRefreshDelay": { + "title": "Backup File Init Next Refresh Delay", + "description": "Delay duration after which to schedule next HTTP refresh when initialized from the backing file.", + "type": "string", + "visibleIf": { + "initializeFromBackupFile": [ + true + ] + } + }, + "requireValidMetadata": { + "title": "Require Valid Metadata?", + "description": "Whether candidate metadata found by the resolver must be valid in order to be returned (where validity is implementation specific, but in SAML cases generally depends on a validUntil attribute.) If this flag is true, then invalid candidate metadata will not be returned.", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "True" + }, + { + "enum": [ + false + ], + "description": "False" + } + ], + "default": true + }, + "failFastInitialization": { + "title": "Fail Fast Initialization?", + "description": "Whether to fail initialization of the underlying MetadataResolverService (and possibly the IdP as a whole) if the initialization of a metadata provider fails. When false, the IdP may start, and will continue to attempt to reload valid metadata if configured to do so, but operations that require valid metadata will fail until it does.", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "True" + }, + { + "enum": [ + false + ], + "description": "False" + } + ], + "default": true + }, + "useDefaultPredicateRegistry": { + "title": "Use Default Predicate Registry?", + "description": "Whether to fail initialization of the underlying MetadataResolverService (and possibly the IdP as a whole) if the initialization of a metadata provider fails. When false, the IdP may start, and will continue to attempt to reload valid metadata if configured to do so, but operations that require valid metadata will fail until it does.", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "True" + }, + { + "enum": [ + false + ], + "description": "False" + } + ], + "default": true + }, + "satisfyAnyPredicates": { + "title": "Satisy Any Predicates?", + "description": "Flag which determines whether predicates used in filtering are connected by a logical 'OR' (true) or by logical 'AND' (false).", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "True" + }, + { + "enum": [ + false + ], + "description": "False" + } + ], + "default": false + } + } +} \ No newline at end of file diff --git a/ui/src/testing/activated-route.stub.ts b/ui/src/testing/activated-route.stub.ts index 511b78744..f41853bf9 100644 --- a/ui/src/testing/activated-route.stub.ts +++ b/ui/src/testing/activated-route.stub.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; -import { convertToParamMap, ParamMap } from '@angular/router'; +import { convertToParamMap, ParamMap, ActivatedRoute } from '@angular/router'; @Injectable() export class ActivatedRouteStub { @@ -13,6 +13,9 @@ export class ActivatedRouteStub { // Test parameters private _testParamMap: ParamMap; + + private _firstChild: ActivatedRouteStub; + get testParamMap() { return this._testParamMap; } set testParamMap(params: {}) { this._testParamMap = convertToParamMap(params); @@ -27,4 +30,12 @@ export class ActivatedRouteStub { get params() { return this.paramMap; } + + get firstChild(): ActivatedRouteStub { + return this._firstChild; + } + + set firstChild(stub: ActivatedRouteStub) { + this._firstChild = stub; + } } From 1db44f06ecba9cd9c83345129d42258a6a22958a Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Thu, 19 Jul 2018 15:44:03 -0400 Subject: [PATCH 13/20] SHIBUI-666 --- .../JPAMetadataResolverServiceImpl.groovy | 34 ++++++++-------- .../MetadataProvidersController.java | 40 ------------------- .../MetadataResolversController.java | 25 ++++++++++++ 3 files changed, 43 insertions(+), 56 deletions(-) delete mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataProvidersController.java diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy index a9485073b..aa2d1e7df 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -46,12 +46,12 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { // TODO: enhance @Override void reloadFilters(String metadataResolverName) { - ChainingMetadataResolver chainingMetadataResolver = (ChainingMetadataResolver)metadataResolver + ChainingMetadataResolver chainingMetadataResolver = (ChainingMetadataResolver) metadataResolver MetadataResolver targetMetadataResolver = chainingMetadataResolver.getResolvers().find { it.id == metadataResolverName } edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver jpaMetadataResolver = metadataResolverRepository.findByName(metadataResolverName) if (targetMetadataResolver && targetMetadataResolver.getMetadataFilter() instanceof MetadataFilterChain) { - MetadataFilterChain metadataFilterChain = (MetadataFilterChain)targetMetadataResolver.getMetadataFilter() + MetadataFilterChain metadataFilterChain = (MetadataFilterChain) targetMetadataResolver.getMetadataFilter() List metadataFilters = new ArrayList<>() @@ -64,7 +64,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { if (entityAttributesFilter.getEntityAttributesFilterTarget().getEntityAttributesFilterTargetType() == EntityAttributesFilterTarget.EntityAttributesFilterTargetType.ENTITY) { rules.put( new EntityIdPredicate(entityAttributesFilter.getEntityAttributesFilterTarget().getValue()), - (List)(List)entityAttributesFilter.getAttributes() + (List) (List) entityAttributesFilter.getAttributes() ) } target.setRules(rules) @@ -76,7 +76,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { if (metadataResolver instanceof RefreshableMetadataResolver) { try { - ((RefreshableMetadataResolver)metadataResolver).refresh() + ((RefreshableMetadataResolver) metadataResolver).refresh() } catch (ResolverException e) { log.warn("error refreshing metadataResolver " + metadataResolverName, e) } @@ -99,15 +99,18 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { 'xsi:schemaLocation': 'urn:mace:shibboleth:2.0:metadata http://shibboleth.net/schema/idp/shibboleth-metadata.xsd urn:mace:shibboleth:2.0:resource http://shibboleth.net/schema/idp/shibboleth-resource.xsd urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.xsd urn:oasis:names:tc:SAML:2.0:metadata http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd' ) { metadataResolverRepository.findAll().each { edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr -> - constructXmlNodeForResolver(mr, delegate) { - MetadataFilter( - 'xsi:type': 'SignatureValidation', - 'requireSignedRoot': 'true', - 'certificateFile': '%{idp.home}/credentials/inc-md-cert.pem' - ) - //TODO: enhance - mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter -> - constructXmlNodeForFilter(filter, delegate) + //TODO: We cannot/do not currently have the code to marshall the internal incommon chaining resolver + if (mr.type != 'BaseMetadataResolver') { + constructXmlNodeForResolver(mr, delegate) { + MetadataFilter( + 'xsi:type': 'SignatureValidation', + 'requireSignedRoot': 'true', + 'certificateFile': '%{idp.home}/credentials/inc-md-cert.pem' + ) + //TODO: enhance + mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter -> + constructXmlNodeForFilter(filter, delegate) + } } } } @@ -324,7 +327,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { resolveViaPredicatesOnly: resolver.reloadableMetadataResolverAttributes?.resolveViaPredicatesOnly ?: null, expirationWarningThreshold: resolver.reloadableMetadataResolverAttributes?.expirationWarningThreshold) { - if(resourceType == SVN) { + if (resourceType == SVN) { MetadataResource( 'xmlns:resource': 'urn:mace:shibboleth:2.0:resource', 'xsi:type': 'resource:SVNResource', @@ -338,8 +341,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { 'proxyUserName': resolver.svnMetadataResource.proxyUserName, 'proxyPassword': resolver.svnMetadataResource.proxyPassword) - } - else if (resourceType == CLASSPATH) { + } else if (resourceType == CLASSPATH) { MetadataResource( 'xmlns:resource': 'urn:mace:shibboleth:2.0:resource', 'xsi:type': 'resource:ClasspathResource', diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataProvidersController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataProvidersController.java deleted file mode 100644 index 538cfa5cf..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataProvidersController.java +++ /dev/null @@ -1,40 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.controller; - -import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; - -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.IOException; -import java.io.StringWriter; - -@Controller -@RequestMapping(value = "/api/metadataProviders") -public class MetadataProvidersController { - private static final Logger logger = LoggerFactory.getLogger(MetadataProvidersController.class); - - @Autowired - MetadataResolverService metadataResolverService; - - @RequestMapping(produces = "application/xml") - public ResponseEntity getXml() throws IOException, TransformerException { - // TODO: externalize - try (StringWriter writer = new StringWriter()) { - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - - transformer.transform(new DOMSource(metadataResolverService.generateConfiguration()), new StreamResult(writer)); - return ResponseEntity.ok(writer.toString()); - } - } -} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java index 1d6c51321..9705ddacc 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java @@ -4,6 +4,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidationService; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -20,7 +21,14 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; import java.io.IOException; +import java.io.StringWriter; import java.net.URI; import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidator.ValidationResult; @@ -36,6 +44,9 @@ public class MetadataResolversController { @Autowired MetadataResolverValidationService metadataResolverValidationService; + @Autowired + MetadataResolverService metadataResolverService; + @ExceptionHandler({InvalidTypeIdException.class, IOException.class, HttpMessageNotReadableException.class}) public ResponseEntity unableToParseJson(Exception ex) { return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.BAD_REQUEST.toString(), ex.getMessage())); @@ -49,6 +60,20 @@ public ResponseEntity getAll() { return ResponseEntity.ok(resolvers); } + @GetMapping(value = "/MetadataResolvers", produces = "application/xml") + @Transactional(readOnly = true) + public ResponseEntity getXml() throws IOException, TransformerException { + // TODO: externalize + try (StringWriter writer = new StringWriter()) { + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + + transformer.transform(new DOMSource(metadataResolverService.generateConfiguration()), new StreamResult(writer)); + return ResponseEntity.ok(writer.toString()); + } + } + @GetMapping("/MetadataResolvers/{resourceId}") @Transactional(readOnly = true) public ResponseEntity getOne(@PathVariable String resourceId) { From b65d1347db437b523450fda8de2084559451b72f Mon Sep 17 00:00:00 2001 From: Bill Smith Date: Thu, 19 Jul 2018 13:00:46 -0700 Subject: [PATCH 14/20] [SHIBUI-635] Fixed broken tests. --- .../DynamicHttpMetadataProviderControllerTests.groovy | 4 ++-- .../FileBackedHttpMetadataProviderControllerTests.groovy | 3 ++- .../LocalDynamicMetadataProviderControllerTests.groovy | 4 ++-- .../admin/ui/controller/MetadataFiltersControllerTests.groovy | 3 ++- .../admin/ui/repository/EnityDescriptorRepositoryTest.groovy | 3 ++- .../FileBackedHttpMetadataResolverRepositoryTests.groovy | 3 ++- .../admin/ui/repository/FilterRepositoryTests.groovy | 3 ++- .../LocalDynamicMetadataResolverRepositoryTests.groovy | 3 ++- .../ui/repository/MetadataResolverRepositoryTests.groovy | 3 ++- .../scheduled/EntityDescriptorFilesScheduledTasksTests.groovy | 3 ++- .../admin/ui/service/EntityIdsSearchServiceTests.groovy | 3 ++- .../IncommonJPAMetadataResolverServiceImplTests.groovy | 3 ++- .../admin/ui/service/JPAEntityServiceImplTests.groovy | 3 ++- .../admin/ui/service/JPAFilterServiceImplTests.groovy | 3 ++- .../ui/service/JPAMetadataResolverServiceImplTests.groovy | 3 ++- 15 files changed, 30 insertions(+), 17 deletions(-) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy index 5b10c9f6e..d11c61629 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy @@ -2,7 +2,7 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.repository.DynamicHttpMetadataResolverRepository @@ -28,7 +28,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class DynamicHttpMetadataProviderControllerTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy index 99457a335..c667c6cfb 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -27,7 +28,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class FileBackedHttpMetadataProviderControllerTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy index 14c925c5c..ed864424c 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy @@ -2,7 +2,7 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.repository.LocalDynamicMetadataResolverRepository @@ -28,7 +28,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class LocalDynamicMetadataProviderControllerTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy index d160411db..0443a7e54 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy @@ -2,6 +2,7 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -35,7 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class MetadataFiltersControllerTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EnityDescriptorRepositoryTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EnityDescriptorRepositoryTest.groovy index e10ff5b0b..eb29bf550 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EnityDescriptorRepositoryTest.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EnityDescriptorRepositoryTest.groovy @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.repository +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -21,7 +22,7 @@ import javax.persistence.EntityManager * A highly unnecessary test so that I can check to make sure that persistence is correct for the model */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class EnityDescriptorRepositoryTest extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepositoryTests.groovy index 5b5d90357..46f582501 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepositoryTests.groovy @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.repository import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -23,7 +24,7 @@ import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttrib import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.HttpMetadataResolverAttributes.HttpCachingType.memory @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") @DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy index 3ef170b9d..d7b59df55 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy @@ -2,6 +2,7 @@ package edu.internet2.tier.shibboleth.admin.ui.repository import com.fasterxml.jackson.databind.ObjectMapper import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter @@ -15,7 +16,7 @@ import spock.lang.Specification import javax.persistence.EntityManager @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class FilterRepositoryTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepositoryTests.groovy index 039f3b324..63ae7018c 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepositoryTests.groovy @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.repository import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter @@ -19,7 +20,7 @@ import javax.persistence.EntityManager import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget.EntityAttributesFilterTargetType.ENTITY @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class LocalDynamicMetadataResolverRepositoryTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTests.groovy index 7179dceee..646d3e4aa 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTests.groovy @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.repository +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -26,7 +27,7 @@ import javax.persistence.EntityManager * Testing persistence of the MetadataResolver models */ @DataJpaTest -@ContextConfiguration(classes = [CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes = [CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class MetadataResolverRepositoryTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy index 73eb2c58c..b687e5c1c 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.scheduled +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -23,7 +24,7 @@ import spock.lang.Specification * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class EntityDescriptorFilesScheduledTasksTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy index 55777e6ec..7b0cc9f4b 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.service +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -14,7 +15,7 @@ import spock.lang.Specification * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class EntityIdsSearchServiceTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy index 24a97ccdb..f2daa7c01 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.service import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget @@ -27,7 +28,7 @@ import static edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers.* @SpringBootTest @DataJpaTest -@ContextConfiguration(classes = [CoreShibUiConfiguration, SearchConfiguration]) +@ContextConfiguration(classes = [CoreShibUiConfiguration, SearchConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class IncommonJPAMetadataResolverServiceImplTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy index 607ed24cc..83a5c76f4 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.service +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -21,7 +22,7 @@ import spock.lang.Specification * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class JPAEntityServiceImplTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy index 11ceb346f..51a6f89bc 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.service +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -18,7 +19,7 @@ import spock.lang.Specification * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class JPAFilterServiceImplTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy index 9c367c1d6..50f944bc9 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.service import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget @@ -41,7 +42,7 @@ import static edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers.generatedX @SpringBootTest @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) From 3676fdf61dc6b2497ba7682f14c3e48af80b17bf Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 19 Jul 2018 14:32:41 -0700 Subject: [PATCH 15/20] Fixed issue with too many load requests, removed logged data --- .../provider/container/provider-edit.component.html | 1 - .../provider/container/provider-wizard.component.ts | 4 +++- ui/src/app/metadata/provider/effect/editor.effect.ts | 3 ++- .../provider/filebacked-http-common.editor.schema.json | 8 ++++---- .../schema/provider/filebacked-http-common.schema.json | 6 +++--- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ui/src/app/metadata/provider/container/provider-edit.component.html b/ui/src/app/metadata/provider/container/provider-edit.component.html index 3778fe440..98c8b0f64 100644 --- a/ui/src/app/metadata/provider/container/provider-edit.component.html +++ b/ui/src/app/metadata/provider/container/provider-edit.component.html @@ -78,7 +78,6 @@ -
{{ status$ | async | json }}
diff --git a/ui/src/app/metadata/provider/container/provider-wizard.component.ts b/ui/src/app/metadata/provider/container/provider-wizard.component.ts index 4a599f33e..014061305 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard.component.ts +++ b/ui/src/app/metadata/provider/container/provider-wizard.component.ts @@ -43,7 +43,9 @@ export class ProviderWizardComponent implements OnDestroy { this.store .select(fromWizard.getCurrentWizardSchema) .subscribe(s => { - this.store.dispatch(new LoadSchemaRequest(s)); + if (s) { + this.store.dispatch(new LoadSchemaRequest(s)); + } }); this.valid$ = this.store.select(fromProvider.getEditorIsValid); this.changes$ = this.store.select(fromProvider.getEntityChanges); diff --git a/ui/src/app/metadata/provider/effect/editor.effect.ts b/ui/src/app/metadata/provider/effect/editor.effect.ts index bbf1bcc48..3146ff91d 100644 --- a/ui/src/app/metadata/provider/effect/editor.effect.ts +++ b/ui/src/app/metadata/provider/effect/editor.effect.ts @@ -8,7 +8,7 @@ import { LoadSchemaFail, EditorActionTypes } from '../action/editor.action'; -import { map, switchMap, catchError, withLatestFrom } from 'rxjs/operators'; +import { map, switchMap, catchError, withLatestFrom, debounceTime } from 'rxjs/operators'; import { of } from 'rxjs'; import { SetDefinition, WizardActionTypes, AddSchema } from '../../../wizard/action/wizard.action'; import { ResetChanges } from '../action/entity.action'; @@ -23,6 +23,7 @@ export class EditorEffects { $loadSchemaRequest = this.actions$.pipe( ofType(EditorActionTypes.LOAD_SCHEMA_REQUEST), map(action => action.payload), + debounceTime(100), switchMap((schemaPath: string) => this.schemaService .get(schemaPath) diff --git a/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json index 77590af65..ce53531bf 100644 --- a/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json +++ b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json @@ -3,7 +3,7 @@ "order": [ "name", "@type", - "id", + "xmlId", "metadataURL", "initializeFromBackupFile", "backingFile", @@ -14,7 +14,7 @@ "satisfyAnyPredicates" ], "required": [ - "id", + "xmlId", "metadataURL" ], "anyOf": [ @@ -50,7 +50,7 @@ }, { "fields": [ - "id", + "xmlId", "metadataURL", "initializeFromBackupFile", "backingFile", @@ -90,7 +90,7 @@ } ] }, - "id": { + "xmlId": { "title": "ID", "description": "Identifier for logging, identification for command line reload, etc.", "type": "string", diff --git a/ui/src/assets/schema/provider/filebacked-http-common.schema.json b/ui/src/assets/schema/provider/filebacked-http-common.schema.json index 65c919add..73414de00 100644 --- a/ui/src/assets/schema/provider/filebacked-http-common.schema.json +++ b/ui/src/assets/schema/provider/filebacked-http-common.schema.json @@ -1,7 +1,7 @@ { "type": "object", "order": [ - "id", + "xmlId", "metadataURL", "initializeFromBackupFile", "backingFile", @@ -11,7 +11,7 @@ "useDefaultPredicateRegistry", "satisfyAnyPredicates" ], - "required": ["id", "metadataURL"], + "required": ["xmlId", "metadataURL"], "dependencies": { "initializeFromBackupFile": {"required": ["backingFile", "backupFileInitNextRefreshDelay"]} }, @@ -40,7 +40,7 @@ } ], "properties": { - "id": { + "xmlId": { "title": "ID", "description": "Identifier for logging, identification for command line reload, etc.", "type": "string", From 07ba5a400e231e4ba4fe6eec328db62175f95bc4 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 23 Jul 2018 07:50:40 -0400 Subject: [PATCH 16/20] Trigger build --- .trigger-jenkins-build | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .trigger-jenkins-build diff --git a/.trigger-jenkins-build b/.trigger-jenkins-build new file mode 100644 index 000000000..e69de29bb From 79aaa9548bf0ea7ae566541b92fc3818654d8253 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 23 Jul 2018 07:58:34 -0400 Subject: [PATCH 17/20] Cleanup --- .trigger-jenkins-build | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .trigger-jenkins-build diff --git a/.trigger-jenkins-build b/.trigger-jenkins-build deleted file mode 100644 index e69de29bb..000000000 From 82dadc88b9235446fb95b022ab4eaee7d572a168 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 23 Jul 2018 12:12:00 -0400 Subject: [PATCH 18/20] SHIBUI-670 WIP --- .../shibboleth/admin/ui/domain/resolvers/MetadataResolver.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java index ab8eda99f..7d0fc03d1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java @@ -54,6 +54,8 @@ public class MetadataResolver extends AbstractAuditable { @Column(unique = true) private String xmlId; + private Boolean enabled = true; + private Boolean requireValidMetadata = true; private Boolean failFastInitialization = true; From c8a54f6d0ac1eed9545c42d87f2f609bb26b2643 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 23 Jul 2018 13:51:04 -0400 Subject: [PATCH 19/20] SHIBUI-670 WIP --- .../admin/ui/service/JPAMetadataResolverServiceImpl.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy index aa2d1e7df..b60c9b0c8 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -100,7 +100,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { ) { metadataResolverRepository.findAll().each { edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr -> //TODO: We cannot/do not currently have the code to marshall the internal incommon chaining resolver - if (mr.type != 'BaseMetadataResolver') { + if ((mr.type != 'BaseMetadataResolver') && (mr.enabled)) { constructXmlNodeForResolver(mr, delegate) { MetadataFilter( 'xsi:type': 'SignatureValidation', From cb54cb67d36ab7c2d2b4480d88db601c83b3a2e6 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 23 Jul 2018 15:29:18 -0400 Subject: [PATCH 20/20] SHIBUI-670: implement enabled flag --- .../JPAMetadataResolverServiceImplTests.groovy | 13 +++++++++++++ backend/src/test/resources/conf/670.xml | 11 +++++++++++ 2 files changed, 24 insertions(+) create mode 100644 backend/src/test/resources/conf/670.xml diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy index 30ad3172a..c6c64e7e1 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy @@ -229,6 +229,19 @@ class JPAMetadataResolverServiceImplTests extends Specification { generatedXmlIsTheSameAsExpectedXml('/conf/520.xml', domBuilder.parseText(writer.toString())) } + def 'test generating disabled MetadataResolver xml snippet'() { + given: 'disabled metadata resolver' + def resolver = testObjectGenerator.filesystemMetadataResolver() + resolver.enabled = false + metadataResolverRepository.save(resolver) + + when: + def generatedXmlDocument = this.metadataResolverService.generateConfiguration() + + then: + generatedXmlIsTheSameAsExpectedXml('/conf/670.xml', generatedXmlDocument) + } + static genXmlSnippet(MarkupBuilder xml, Closure xmlNodeGenerator) { xml.MetadataProvider('id': 'ShibbolethMetadata', 'xmlns': 'urn:mace:shibboleth:2.0:metadata', diff --git a/backend/src/test/resources/conf/670.xml b/backend/src/test/resources/conf/670.xml new file mode 100644 index 000000000..952c86ee6 --- /dev/null +++ b/backend/src/test/resources/conf/670.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file