Skip to content

Commit

Permalink
Migrating existing documentation for the sample (#36)
Browse files Browse the repository at this point in the history
* How to change service configuration
* Guide how to add new REST controller
* Web security configuration for the guide
* Swagger annotations documentation
* Automatic message instance ID
* Complete error handling documentation
* Handle 401 Unauthorized with the standard message format
* Fix problem when `type java`  does not find java
* Fix missing WWW-Authenticate header and add a test for it
* Check .so before packaging JAR
* Disabling Azure pipelines
* Download .so files if missing

Signed-off-by: Petr Plavjanik <plavjanik@gmail.com>
  • Loading branch information
plavjanik committed Sep 13, 2019
1 parent e4e4e54 commit 4db8fd9
Show file tree
Hide file tree
Showing 58 changed files with 1,890 additions and 187 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ jobs:
- zowe-apiservice-gradle-{{ checksum "build.gradle" }}
- run:
name: Building Java Code using Gradle
command: ./gradlew build
command: |
./gradlew build
- codecov/upload
- save_cache:
paths:
Expand All @@ -34,7 +35,6 @@ jobs:
- run:
name: Upload Java Libraries to Bintray using Gradle
command: |
scripts/download-commons-so.sh
./gradlew build bintrayUpload
- run:
name: Upload Sample Source Code to GitHub Release
Expand Down
27 changes: 0 additions & 27 deletions azure-pipelines.yml

This file was deleted.

4 changes: 0 additions & 4 deletions scripts/download-commons-so.sh

This file was deleted.

5 changes: 5 additions & 0 deletions scripts/publish-sample-so.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# TODO: Replace by build on z/OS in CI
cd zowe-rest-api-sample-spring
zowe-api-dev zosbuild
base64 src/main/resources/lib/libwtojni.so | gist -f libwtojni
19 changes: 15 additions & 4 deletions zowe-api-dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,25 @@ Provides convinence functions to build native code, deploy REST API, and start i

## Installation

### From NPM (for users)

```bash
npm -g install @zowedev/zowe-api-dev
```

### From sources (for developers)

```bash
https://github.com/zowe/sample-spring-boot-api-service
cd sample-spring-boot-api-service
cd zowe-api-dev
npm link
```

## Usage

See [zowe-api-dev - Zowe API Development CLI Tool](/zowe-rest-api-sample-spring/docs/devtool.md).

```txt
$ zowe-api-dev
Expand All @@ -27,7 +42,3 @@ COMMANDS
zfs initialize user configuration file
zosbuild build z/OS source on z/OS UNIX
```

## Usage

See [zowe-api-dev - Zowe API Development CLI Tool](/zowe-rest-api-sample-spring/docs/devtool.md).
4 changes: 2 additions & 2 deletions zowe-rest-api-commons-spring/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ git tag v0.0.1
git push origin v0.0.1
```

## Publising to Maven Local
## Publishing to Maven Local

`./gradlew publishToMavenLocal`

Expand All @@ -21,7 +21,7 @@ You need to provide your credentials to Bintray. You can get it from <https://bi

`BINTRAY_USER=user BINTRAY_API_KEY=apikey ./gradlew uploadBintray`

## Using the library
## Using the Library

```gradle
plugins {
Expand Down
19 changes: 15 additions & 4 deletions zowe-rest-api-commons-spring/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ ext {
license {
header rootProject.file('.licence/EPL-2.0-licence-header.txt')
ext.year = Calendar.getInstance().get(Calendar.YEAR)
excludes(["**/*.yml", "**/*.json", "**/*.sh", "**/*.txt", "**/*.p12", "**/*.xml", "**/*.jsp", "**/*.html", "**/*.jks"])
excludes(["**/*.yml", "**/*.json", "**/*.sh", "**/*.txt", "**/*.p12", "**/*.xml", "**/*.jsp", "**/*.html", "**/*.jks", "**/*.so", "**/*.md"])
mapping {
java = 'SLASHSTAR_STYLE'
}
Expand All @@ -43,14 +43,25 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
implementation "com.ca.mfaas.sdk:mfaas-integration-enabler-java:$zoweApimlVersion"
implementation "com.ca.mfaas.sdk:mfaas-common-service-core:$zoweApimlVersion"
// implementation 'io.springfox:springfox-swagger2:2.9.2'
// implementation 'io.springfox:springfox-swagger-ui:2.9.2'
compileOnly 'org.projectlombok:lombok:1.18.8'
annotationProcessor 'org.projectlombok:lombok:1.18.8'
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testImplementation "net.javacrumbs.json-unit:json-unit-fluent:2.8.0"
}

task downloadSharedObject {
doFirst {
def filePath = 'src/main/resources/lib/libzowe-commons-secur.so'
def f = new File(projectDir, filePath)
if (!f.exists()) {
logger.warn "Shared object $filePath is missing. Dowloading it from GitHub Gist. Run `zowe-api-dev zosbuild` in directory $projectDir to build it yourself"
f.append(new URL(securShareObjectUrl).text.decodeBase64())
}
}
}

jar.dependsOn downloadSharedObject

jar {
manifest {
attributes('Implementation-Title': project.name,
Expand Down
1 change: 1 addition & 0 deletions zowe-rest-api-commons-spring/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
securShareObjectUrl=https://gist.githubusercontent.com/plavjanik/eb09a5f74844e2d6d305f94a6885f678/raw/8ed5ba65c34e35f5ef389c45995467aee5bc9d9f/libzowe-commons-secur.so
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
{
"properties": [
{
"name": "apiml",
"type": "java.util.Map",
"description": "Zowe API Mediation Layer related configuration"
},
{
"name": "apiml.service",
"type": "java.util.Map",
"description": "Definition of the API service"
},
{
"name": "apiml.service.serviceId",
"type": "java.lang.String",
"description": "Specifies the service instance identifier that is registered in the API Mediation Layer installation.\nThe service ID is used in the URL for routing to the API service through the gateway.\nThe service ID uniquely identifies the service in the API Mediation Layer.\nThe system administrator at the customer site defines this parameter.\n\n**Important!** Ensure that the service ID is set properly with the following considerations:\n\n* When two API services use the same service ID, the API gateway considers the services to be clones (two instances for the same service). An incoming API request can be routed to either of them.\n* The same service ID should be set only for multiple API service instances for API scalability.\n* The service ID value must contain only lowercase alphanumeric characters.\n* The service ID cannot contain more than 40 characters.\n* The service ID is linked to security resources. Changes to the service ID require an update of security resources.\n\n**Examples:**\n* If the customer system administrator sets the service ID to `sysviewlpr1`,\nthe API URL in the API Gateway appears as the following URL:\n\n `https://gateway:port/api/v1/sysviewlpr1/...`\n\n* If customer system administrator sets the service ID to `vantageprod1`,\nthe API URL in the API Gateway appears as the following URL:\n\n http://gateway:port/api/v1/vantageprod1/..."
},
{
"name": "apiml.service.title",
"type": "java.lang.String",
"description": "Specifies the human readable name of the API service instance (for example, \"Endevor Prod\" or \"Sysview LPAR1\"). This value is displayed in the API catalog when a specific API service instance is selected. This parameter is externalized and set by the customer system administrator.\n\n**Tip:** We recommend that you provide a specific default value of the `title`.\nUse a title that describes the service instance so that the end user knows the specific purpose of the service instance."
},
{
"name": "apiml.service.description",
"type": "java.lang.String",
"description": "Specifies a short description of the API service.\n\n**Example:** \"CA Endevor SCM - Production Instance\" or \"CA SYSVIEW running on LPAR1\".\n\nThis value is displayed in the API Catalog when a specific API service instance is selected. This parameter is externalized and set by the customer system administrator. \n\n**Tip:** Describe the service so that the end user knows the function of the service."
},
{
"name": "apiml.service.homePageRelativeUrl",
"type": "java.lang.String",
"description": "Specifies the relative path to the homepage of your service. The path should start with `/`.\nIf your service has no homepage, omit this parameter.\n\n**Examples:**\n* `homePageRelativeUrl: /` The service has homepage with URL `${baseUrl}/`\n* `homePageRelativeUrl: /ui/` The service has homepage with URL `${baseUrl}/ui/`\n* `homePageRelativeUrl: ` The service has homepage with URL `${baseUrl}`"
},
{
"name": "apiml.service.statusPageRelativeUrl",
"type": "java.lang.String",
"description": ""
},
{
"name": "apiml.service.healthCheckRelativeUrl",
"type": "java.lang.String",
"description": "Specifies the relative path to the health check endpoint of your service.\nStart this URL with `/`. If your service does not have a health check endpoint, omit this parameter.\n\n**Example:**\n* `healthCheckRelativeUrl: actuator/health`. This results in the URL:\n`${baseUrl}/actuator/health`"
},
{
"name": "apiml.service.homePageRelativeUrl",
"type": "java.lang.String",
"description": ""
},
{
"name": "apiml.service.baseUrl",
"type": "java.lang.String",
"description": "Specifies the full base URLs to your service to the REST resource. It will be the prefix for the following URLs:\n\n* **homePageRelativeUrl**\n* **statusPageRelativeUrl**\n* **healthCheckRelativeUrl**\n\n**Examples:**\n* `- http://host:port/filemasterplus` for an HTTP service\n* `- https://host:port/endevor` for an HTTPS service\n\nIt is required when your service registers to Zowe API Mediation Layer or provides full URLs to itself in the API responses.\n\nIt is recommended to keep the original value as in the sample: `${apiml.service.scheme}://${apiml.service.hostname}:${server.port}/`"
},
{
"name": "apiml.service.scheme",
"type": "java.lang.String",
"description": "URI scheme that is used to access to server. It can be `https` for TLS protected servers or `http`"
},
{
"name": "apiml.service.catalogUiTile",
"type": "java.lang.String",
"description": "This section defines API Mediation Layer tile that is used to group similar services together. Set values in this section to define the default tile for your API service."
},
{
"name": "apiml.service.catalogUiTile.id",
"type": "java.lang.String",
"description": "Specifies the unique identifier for the API services group.\nThis is the grouping value used by the API Mediation Layer to group multiple API services\ntogether into *tiles*.\nEach unique identifier represents a single API Catalog UI dashboard tile.\nSpecify the value based on the ID of the defined tile."
},
{
"name": "apiml.service.catalogUiTile.title",
"type": "java.lang.String",
"description": "Specifies the title of the API services product family. This value is displayed in the API catalog UI dashboard as the tile title."
},
{
"name": "apiml.service.catalogUiTile.description",
"type": "java.lang.String",
"description": "Specifies the detailed description of the API Catalog UI dashboard tile.\nThis value is displayed in the API catalog UI dashboard as the tile description."
},
{
"name": "apiml.service.catalogUiTile.version",
"type": "java.lang.String",
"description": "Specifies the version of the tile. This allows you to provide an updated definition in future. The highest version will be displayed in the API Catalog. Use semantic versioning."
},
{
"name": "apiml.service.routes",
"type": "java.lang.String",
"description": "The routing rules between the API Gateway and your service.\n\n* **routes.gatewayUrl**\n\n Both _gatewayUrl_ and _serviceUrl_ parameters specify how the API service endpoints are mapped to the API\n gateway endpoints. The _gatewayUrl_ parameter sets the target endpoint on the gateway.\n\n* **routes.serviceUrl**\n\n Both _gatewayUrl_ and _serviceUrl_ parameters specify how the API service endpoints are mapped to the API\n gateway endpoints. The _serviceUrl_ parameter points to the target endpoint on the gateway."
},
{
"name": "apiml.service.apiInfo",
"type": "java.lang.String",
"description": "This section defines APIs that are provided by the service. Currently, only one API is supported.\n\n* **apiInfo.apiId**\n\n Specifies the API identifier that is registered in the API Mediation Layer installation.\n The API ID uniquely identifies the API in the API Mediation Layer.\n The same API can be provided by multiple services. The API ID can be used\n to locate the same APIs that are provided by different services.\n The creator of the API defines this ID.\n The API ID needs to be a string of up to 64 characters\n that uses lowercase alphanumeric characters and a dot: `.`.\n We recommend that you use your organization as the prefix.\n\n **Examples:**\n\n * `org.zowe.file`\n * `com.ca.sysview`\n * `com.ibm.zosmf`\n\n* **apiInfo.gatewayUrl**\n\n The base path at the API gateway where the API is available. Ensure that this path is\n the same as the _gatewayUrl_ value in the _routes_ sections.\n\n* **apiInfo.swaggerUrl**\n\n (Optional) Specifies the HTTP or HTTPS address where the Swagger JSON document is available. \n\n* **apiInfo.documentationUrl**\n\n (Optional) Specifies a URL to a website where external documentation is provided.\n This can be used when _swaggerUrl_ is not provided.\n\n* **apiInfo.version**\n\n (Optional) Specifies the actual version of the API in [semantic versioning](https://semver.org/) format. This can be used when _swaggerUrl_ is not provided."
},
{
"name": "apiml.service.",
"type": "java.lang.String",
"description": ""
},
{
"name": "server.ssl.verifySslCertificatesOfServices",
"type": "java.lang.String",
"description": "Validation of the certificates of services that are accessed from this service (e.g. Discovery Service)"
},
{
"name": "springfox.documentation.swagger.v2.path",
"type": "java.lang.String",
"description": "URL where the Swagger API documentation JSON document will be provided"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public FilterReply decide(Marker marker, Logger logger, Level level, String form
message = ExceptionUtils.getRootCauseMessage(t);
}
if ((message != null) && !message.isEmpty()) {
log.error(CommonsErrorService.getReadableMessage("org.zowe.commons.apiml.unableToRegister", message));
log.error(CommonsErrorService.get().getReadableMessage("org.zowe.commons.apiml.unableToRegister", message));
logOriginalError(t);
}
}
Expand All @@ -57,7 +57,7 @@ boolean shouldExit(Level level, Throwable t) {
for (String s : ExceptionUtils.getStackFrames(t)) {
if ((s.indexOf(".ApiMediationClient") >= 0)
|| (s.indexOf("com.netflix.discovery.DiscoveryClient") > 0)) {
log.error(CommonsErrorService.getReadableMessage("org.zowe.commons.apiml.apimlCertificateNotTrusted",
log.error(CommonsErrorService.get().getReadableMessage("org.zowe.commons.apiml.apimlCertificateNotTrusted",
t.getMessage()));
logOriginalError(t);
if (SpringContext.getApplicationContext() == null) {
Expand All @@ -69,7 +69,7 @@ boolean shouldExit(Level level, Throwable t) {
for (String s : ExceptionUtils.getStackFrames(t)) {
if ((s.indexOf(".ApiMediationClient") >= 0)
|| (s.indexOf("com.netflix.discovery.DiscoveryClient") > 0)) {
log.error(CommonsErrorService.getReadableMessage("org.zowe.commons.apiml.serviceCertificateNotTrusted",
log.error(CommonsErrorService.get().getReadableMessage("org.zowe.commons.apiml.serviceCertificateNotTrusted",
t.getMessage()));
logOriginalError(t);
if (SpringContext.getApplicationContext() == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,16 @@
*/
package org.zowe.commons.error;

import com.ca.mfaas.error.ErrorService;
import com.ca.mfaas.error.impl.ErrorServiceImpl;
import com.ca.mfaas.rest.response.ApiMessage;
import org.zowe.commons.error.ErrorService;
import org.zowe.commons.error.ErrorServiceImpl;

public final class CommonsErrorService {
private static ErrorService errorService = new ErrorServiceImpl("/commons-messages.yml");
private static ErrorService errorService = new ErrorServiceImpl();

private CommonsErrorService() {}
private CommonsErrorService() {
}

public static ErrorService get() {
return errorService;
}

public static String getReadableMessage(String key, Object... parameters) {
return CommonsErrorService.get().createApiMessage(key, parameters).getMessages().get(0).toReadableText();
}

public static String getReadableMessage(ApiMessage apiMessage) {
return apiMessage.getMessages().get(0).toReadableText();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/
package org.zowe.commons.error;

/**
* Exception thrown when a message is already defined before
*/
public class DuplicateMessageException extends RuntimeException {
private static final long serialVersionUID = -3407108866724093071L;

public DuplicateMessageException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/
package org.zowe.commons.error;

import org.zowe.commons.rest.response.MessageType;

public class ErrorMessage {
private String key;
private String number;
private MessageType type;
private String text;

public ErrorMessage() {
}

public ErrorMessage(String key, String number, MessageType type, String text) {
this.key = key;
this.number = number;
this.type = type;
this.text = text;
}

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

public String getNumber() {
return number;
}

public void setNumber(String number) {
this.number = number;
}

public MessageType getType() {
return type;
}

public void setType(MessageType type) {
this.type = type;
}

public String getText() {
return text;
}

public void setText(String text) {
this.text = text;
}
}
Loading

0 comments on commit 4db8fd9

Please sign in to comment.