Skip to content

Commit

Permalink
Merge pull request #35 from apiman/release/v0.3.1
Browse files Browse the repository at this point in the history
Release v0.3.1
  • Loading branch information
outofcoffee authored Mar 24, 2018
2 parents 75bc975 + 8a2aa4d commit e35c0f0
Show file tree
Hide file tree
Showing 17 changed files with 176 additions and 111 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [0.3.1] - 2018-03-24
### Added
- Adds support for applying multiple API declarations at once.
- Improves null safety of headless configuration generator.

## [0.3.0] - 2018-03-08
### Added
- Allow gateway to be driven directly using declarations (Marc Savy). See _Changes_ section for details of changes to
Expand Down
30 changes: 20 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ Typically, you will administer the Manager, then publish your changes to the Gat

### Apply declaration

A 'declaration' is a configuration file specifying Gateways, APIs and policies. See the _Declarative API management_
section for more information.

apiman manager apply [args...]

--declarationFile (-f) PATH : Declaration file
Expand All @@ -301,20 +304,27 @@ The following commands are available, when administering the Gateway directly:
--debug: Log at DEBUG level
--help, -h: Display usage only

## Tips and tricks

When working with declaration files, in any of the following scenarios:

* declarative gateway
* declarative manager
* generate headless gateway configuration

...the commands support repetition of the `--declarationFile` argument.

This means you can do things like:

./apiman manager apply --declarationFile=/path/to/file1.yml --declarationFile=/path/to/file2.yml

...and the declarations will be merged in the order the files are provided.

# Recent changes and Roadmap

For recent changes see the [Changelog](CHANGELOG.md).

## Roadmap

* Support reading management API configuration from environment variables
* Better support for non-public APIs
* Support deletion
* Support for retiring published APIs
* Option to skip or fail for existing items in declarative mode
* Docs - split examples into separate file
* Docs - split detailed API usage into separate file
* Docs - simplify README examples
See the [Roadmap](ROADMAP.md) document for future plans.

# Building

Expand Down
13 changes: 13 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Roadmap
=======

* Support reading management API configuration from environment variables
* Better support for non-public APIs
* Support deletion
* Support for retiring published APIs
* Option to skip or fail for existing items in declarative mode
* Docs - split examples into separate file
* Docs - split detailed API usage into separate file
* Docs - simplify README examples

If you have other suggestions, please raise an issue!
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

group 'io.apiman.cli'
version '0.3.0'
version '0.3.1'

buildscript {
repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterDescription;
import com.beust.jcommander.ParameterException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Injector;
import io.apiman.cli.exception.CommandException;
Expand Down Expand Up @@ -59,7 +60,7 @@ public abstract class AbstractCommand implements Command {
* land into it, throwing a custom error message instead.
*/
@Parameter(hidden=true)
private List<String> mainParameter = new ArrayList<>();
private List<String> mainParameter = Lists.newArrayList();

/**
* The parent Command (<code>null</code> if root).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static BaseDeclaration loadDeclaration(Path path, ObjectMapper mapper, Ma
return declaration;

} catch (IOException e) {
throw new DeclarativeException(e);
throw new DeclarativeException("Unable to load declaration: " + path, e);
}
}

Expand All @@ -90,7 +90,7 @@ public static BaseDeclaration loadDeclaration(Path path, ObjectMapper mapper, Ma
* @throws IOException
*/
private static BaseDeclaration loadDeclaration(ObjectMapper mapper, String unresolved,
Map<String, String> properties) throws IOException {
Map<String, String> properties) throws IOException {

final String resolved = BeanUtil.resolvePlaceholders(unresolved, properties);
LOGGER.trace("Declaration file after resolving {} placeholders: {}", properties.size(), resolved);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import static java.util.Optional.ofNullable;

Expand All @@ -51,7 +52,7 @@ public abstract class AbstractApplyCommand extends AbstractFinalCommand {
protected static final String JSON_EXTENSION = ".json";

@Parameter(names = {"--declarationFile", "-f"}, description = "Declaration file")
protected Path declarationFile;
protected List<Path> declarationFiles;

@Parameter(names = "-P", description = "Set property (key=value)")
protected List<String> properties;
Expand All @@ -70,16 +71,26 @@ public AbstractApplyCommand() {
@Override
public void performFinalAction(JCommander parser) throws CommandException {
try {
applyDeclaration(loadDeclaration());
applyDeclarations();
} catch (Exception e) {
throw new CommandException("Error applying declaration", e);
}
}

/**
* @return load all the {@link #declarationFiles}
*/
private List<BaseDeclaration> loadDeclarations() {
return declarationFiles.stream()
.map(this::loadDeclaration)
.collect(Collectors.toList());
}

/**
* Load and then apply the Declaration.
* @param declarationFile
*/
private BaseDeclaration loadDeclaration() {
private BaseDeclaration loadDeclaration(Path declarationFile) {
final Map<String, String> parsedProperties = BeanUtil.parseReplacements(properties);

// check for properties file
Expand All @@ -106,11 +117,11 @@ private BaseDeclaration loadDeclaration() {
return declaration;
}

public void applyDeclaration() {
applyDeclaration(loadDeclaration());
public void applyDeclarations() {
applyDeclarations(loadDeclarations());
}

protected abstract void applyDeclaration(BaseDeclaration declaration);
protected abstract void applyDeclarations(List<BaseDeclaration> declaration);

protected BaseDeclaration loadDeclaration(Path declarationFile, Map<String, String> parsedProperties) {
// parse declaration
Expand All @@ -122,8 +133,8 @@ protected BaseDeclaration loadDeclaration(Path declarationFile, Map<String, Stri
}
}

public void setDeclarationFile(Path declarationFile) {
this.declarationFile = declarationFile;
public void setDeclarationFiles(List<Path> declarationFiles) {
this.declarationFiles = declarationFiles;
}

public void setProperties(List<String> properties) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import io.apiman.cli.command.declarative.command.AbstractApplyCommand;
import io.apiman.cli.command.declarative.model.BaseDeclaration;
Expand All @@ -37,7 +38,9 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
Expand Down Expand Up @@ -85,23 +88,33 @@ public void setPolicyResolver(PolicyResolver policyResolver) {
public void setJsonWriter(JsonWriter jsonWriter) { this.jsonWriter = jsonWriter; }

@Override
protected void applyDeclaration(BaseDeclaration declaration) {
GatewayApiDataModel dataModel = new GatewayApiDataModel(declaration, policyResolver);
protected void applyDeclarations(List<BaseDeclaration> declarations) {
final Map<DeclarativeGateway, List<Api>> gatewaysMap = new HashMap<>();

if (dataModel.getGatewaysMap().size() > 1) {
declarations.forEach(declaration -> {
final GatewayApiDataModel dataModel = new GatewayApiDataModel(declaration, policyResolver);

dataModel.getGatewayToApisMap().forEach((gateway, apis) -> {
final List<Api> gatewayApis = gatewaysMap.getOrDefault(gateway, Lists.newArrayList());
gatewayApis.addAll(apis);
gatewaysMap.put(gateway, gatewayApis);
});
});

if (gatewaysMap.keySet().size() > 1) {
LOGGER.info("{} gateway targets exist in declaration. " +
"Multiple configurations will be generated as a result.", dataModel.getGatewaysMap().size());
"Multiple configurations will be generated as a result.", gatewaysMap.keySet().size());
}

LOGGER.debug("Generating {} JSON configuration(s)", dataModel.getGatewaysMap().size());
generateJsonConfig(dataModel);
LOGGER.debug("Generating {} JSON configuration(s)", gatewaysMap.keySet().size());
generateJsonConfig(gatewaysMap);
}

private void generateJsonConfig(GatewayApiDataModel dataModel) {
boolean directorySpecified = (outputFiles.size() == 1 && Files.isDirectory(outputFiles.get(0)));
private void generateJsonConfig(Map<DeclarativeGateway, List<Api>> gatewaysMap) {
final boolean directorySpecified = (outputFiles.size() == 1 && Files.isDirectory(outputFiles.get(0)));

dataModel.getGatewayToApisMap().forEach((gateway, apis) -> {
HeadlessConfigBean bean = new HeadlessConfigBean(apis, Collections.emptyList()); // TODO look up clients for the gateway
gatewaysMap.forEach((gateway, apis) -> {
final HeadlessConfigBean bean = new HeadlessConfigBean(apis, Collections.emptyList()); // TODO look up clients for the gateway

if (useStdout || outputFiles.isEmpty()) {
System.out.println(bean.toJson());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ public GatewayApplyCommand(GatewayApiFactory apiFactory,
}

@Override
protected void applyDeclaration(BaseDeclaration declaration) {
GatewayApiDataModel dataModel = new GatewayApiDataModel(declaration, policyResolver);
// Do gateway status checks: Tests whether gateways exist and advertise as up/available.
doGatewayStatusChecks(dataModel);
// Finally, publish.
publishAll(dataModel);
protected void applyDeclarations(List<BaseDeclaration> declarations) {
declarations.forEach(declaration -> {
GatewayApiDataModel dataModel = new GatewayApiDataModel(declaration, policyResolver);
// Do gateway status checks: Tests whether gateways exist and advertise as up/available.
doGatewayStatusChecks(dataModel);
// Finally, publish.
publishAll(dataModel);
});
}

private void doGatewayStatusChecks(GatewayApiDataModel dataModel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.apiman.cli.gatewayapi.model;

import com.google.common.collect.Lists;
import io.apiman.cli.command.api.model.ApiGateway;
import io.apiman.cli.command.api.model.EndpointProperties;
import io.apiman.cli.command.declarative.model.BaseDeclaration;
Expand All @@ -35,7 +36,6 @@
import org.apache.logging.log4j.Logger;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
Expand All @@ -44,6 +44,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;

/**
Expand Down Expand Up @@ -89,23 +90,25 @@ public Map<DeclarativeGateway, List<Api>> getGatewayToApisMap() {
}

private void buildDataModel() {
orgId = declaration.getOrg().getName();
LOGGER.debug("Organization ID: {}", orgId);

gatewaysMap = declaration.getSystem().getGateways()
.stream()
.collect(Collectors.toMap(Gateway::getName, gw -> gw));
gatewaysMap = ofNullable(declaration.getSystem().getGateways()).orElse(emptyList())
.stream()
.collect(Collectors.toMap(Gateway::getName, gw -> gw));

LOGGER.debug("Gateways map: {}", gatewaysMap);

pluginMap = buildPluginMap(declaration);
pluginMap = buildPluginMap(ofNullable(declaration.getSystem().getPlugins()).orElse(emptyList()));
LOGGER.debug("Plugin map: {}", pluginMap);

apiToGatewaysMap = buildApisToGatewayMap(declaration);
LOGGER.debug("APIs to Gateway map: {}", apiToGatewaysMap);
ofNullable(declaration.getOrg()).ifPresent(org -> {
orgId = org.getName();
LOGGER.debug("Organization ID: {}", orgId);

gatewayToApisMap = buildApisOnGatewaysMap();
LOGGER.debug("Gateways to APIs map: {}", gatewayToApisMap);
apiToGatewaysMap = buildApisToGatewayMap(declaration);
LOGGER.debug("APIs to Gateway map: {}", apiToGatewaysMap);

gatewayToApisMap = buildApisOnGatewaysMap();
LOGGER.debug("Gateways to APIs map: {}", gatewayToApisMap);
});
}

private Map<DeclarativeGateway, List<Api>> buildApisOnGatewaysMap() {
Expand All @@ -115,18 +118,18 @@ private Map<DeclarativeGateway, List<Api>> buildApisOnGatewaysMap() {
Api addApi = entry.getKey();
List<DeclarativeGateway> gateways = entry.getValue();
for (DeclarativeGateway gateway : gateways) {
List<Api> apiList = outMap.getOrDefault(gateway, new ArrayList<>());
List<Api> apiList = outMap.getOrDefault(gateway, Lists.newArrayList());
apiList.add(addApi);
outMap.put(gateway, apiList);
}
}
return outMap;
}

private Map<String, Plugin> buildPluginMap(BaseDeclaration declaration) {
private Map<String, Plugin> buildPluginMap(List<Plugin> plugins) {
Map<String, Plugin> pluginMap = new LinkedHashMap<>();
List<Plugin> pluginsList = ofNullable(declaration.getSystem().getPlugins())
.orElse(Collections.emptyList());
List<Plugin> pluginsList = ofNullable(plugins)
.orElse(emptyList());

pluginsList
.forEach(plugin -> {
Expand Down Expand Up @@ -186,7 +189,7 @@ private Map.Entry<Api, List<DeclarativeGateway>> initialiseApi(DeclarativeApi mo
}

private List<Policy> buildPolicyChain(List<DeclarativePolicy> policies) {
return ofNullable(policies).orElse(Collections.emptyList()).stream()
return ofNullable(policies).orElse(emptyList()).stream()
.map(declarativePolicy -> {
Policy policy = new Policy();
policy.setPolicyImpl(determinePolicyImpl(declarativePolicy));
Expand Down
Loading

0 comments on commit e35c0f0

Please sign in to comment.