Skip to content

Commit

Permalink
Add regex validate check
Browse files Browse the repository at this point in the history
  • Loading branch information
lnash94 committed May 3, 2023
1 parent 2c16fee commit 0ee787e
Show file tree
Hide file tree
Showing 11 changed files with 261 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import ballerina/constraint;
public type PersonHobbyItemsString string;

public type Person record {
@constraint:String {maxLength: 14, pattern: re `^[a-zA-Z0-9]*$`}
@constraint:String {maxLength: 14}
string name?;
PersonHobbyItemsString[] hobby?;
@constraint:Int {maxValue: 5}
int id;
float salary?;
decimal net?;
@constraint:String {pattern: re `^[a-zA-Z0-9_]+$`}
string salary?;
string net?;
int count?;
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ components:
properties:
name:
type: string
pattern: "^[a-zA-Z0-9]*$"
maxLength: 14 # Use case 01 : Annotations on a record field
pattern: ^(?!(.*[\"\*\\\>\<\?\/\:\|]+.*)|(.*[\.]?.*[\.]+$)|(.*[ ]+$)) # Use case 01 : This pattern can not support via Ballerina regex
maxLength: 14
hobby:
type: array
items:
Expand All @@ -49,9 +49,10 @@ components:
type: integer
maximum: 5
salary:
type: number
format: float
type: string
pattern: ^[a-zA-Z0-9_]+$
net:
type: number
type: string
pattern: ^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$ # Use case 02 : This pattern can not support via Ballerina regex
count:
type: integer
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ components:
type: object
properties:
username:
pattern: ^[a-zA-Z0-9_\-]+$
type: string
description: Username of the account
nickname:
Expand Down
1 change: 1 addition & 0 deletions openapi-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ dependencies {
implementation "org.ballerinalang:formatter-core"
implementation "org.ballerinalang:ballerina-cli"
implementation "org.ballerinalang:ballerina-tools-api"
implementation "org.ballerinalang:ballerina-runtime"
implementation "io.ballerina.stdlib:http-native"
implementation "com.google.code.findbugs:jsr305"
testImplementation "org.testng:testng"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
import io.ballerina.openapi.core.generators.schema.ballerinatypegenerators.TypeGenerator;
import io.ballerina.openapi.core.generators.schema.ballerinatypegenerators.UnionTypeGenerator;
import io.ballerina.openapi.core.generators.schema.model.GeneratorMetaData;
import io.ballerina.runtime.api.values.BError;
import io.ballerina.runtime.internal.regexp.RegExpFactory;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.BooleanSchema;
Expand Down Expand Up @@ -421,16 +423,24 @@ private static List<String> getStringAnnotFields(StringSchema stringSchema) {
String value = stringSchema.getPattern();
// This is to check whether the pattern is valid or not.
PatternSyntaxException exc = null;
// TODO: This temp fix will be removed with available with the new Regex API.
// https://github.com/ballerina-platform/ballerina-lang/issues/40328
// https://github.com/ballerina-platform/ballerina-lang/issues/40318
try {
Pattern.compile(value);
RegExpFactory.parse(value);
} catch (PatternSyntaxException e) {
exc = e;
} catch (BError err) {
//The value was assigned as 0 to the index due to the creation of a PatternSyntaxException.
//This will be resolved with the availability of the new Regex API.
exc = new PatternSyntaxException(err.getMessage(), value, 0);
}
if (exc == null) {
String fieldRef = "pattern: re" + "`" + value + "`";
fields.add(fieldRef);
} else {
OUT_STREAM.printf("WARNING: Invalid pattern: " + value);
OUT_STREAM.printf("WARNING: ballerina can not support pattern: %s %n", value);
}
}
return fields;
Expand Down
2 changes: 2 additions & 0 deletions openapi-core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
requires io.ballerina.stdlib.http;
requires io.ballerina.cli;
requires io.ballerina.tools.api;
requires io.ballerina.runtime;
requires io.ballerina.formatter.core;
requires io.ballerina.openapi.converter;
requires io.swagger.v3.core;
Expand All @@ -40,6 +41,7 @@
requires swagger.parser.core;
requires swagger.parser.v3;
requires org.apache.commons.lang3;

exports io.ballerina.openapi.core.generators.service;
exports io.ballerina.openapi.core.model;
exports io.ballerina.openapi.core.exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static io.ballerina.openapi.extension.build.ValidatorTests.WHITESPACE_PATTERN;
import static io.ballerina.openapi.idl.client.IDLClientGenPluginTests.DISTRIBUTION_FILE_NAME;
import static io.ballerina.openapi.idl.client.IDLClientGenPluginTests.TEST_RESOURCE;

Expand Down Expand Up @@ -197,4 +200,17 @@ public boolean accept(File pathname) {
});
return matchingFiles;
}

public static void assertOnErrorStream(Process process, String msg) throws IOException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
Stream<String> logLines = br.lines();
String generatedLog = logLines.collect(Collectors.joining("\n"));
logLines.close();
String formattedGeneratedLog = (generatedLog.trim()).replaceAll(WHITESPACE_PATTERN, "");
String formattedMessage = msg.trim().replaceAll(WHITESPACE_PATTERN, "");
Assert.assertTrue(formattedGeneratedLog.contains(formattedMessage),
String.format("compiler output doesn't contain the expected" +
" output: %s : generated output : %s", msg, generatedLog));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.ballerina.openapi.cmd;

import io.ballerina.openapi.OpenAPITest;
import io.ballerina.openapi.TestUtil;
import org.apache.commons.lang3.StringUtils;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;

import static io.ballerina.openapi.TestUtil.DISTRIBUTIONS_DIR;
import static io.ballerina.openapi.TestUtil.OUT;
import static io.ballerina.openapi.TestUtil.RESOURCES_PATH;
import static io.ballerina.openapi.TestUtil.TEST_DISTRIBUTION_PATH;
import static io.ballerina.openapi.TestUtil.assertOnErrorStream;

/**
* This test class is for storing the schema related integrations to negative scenarios.
*/
public class SchemaGenerationNegativeTests extends OpenAPITest {
public static final String DISTRIBUTION_FILE_NAME = DISTRIBUTIONS_DIR.toString();
public static final Path TEST_RESOURCE = Paths.get(RESOURCES_PATH.toString() + "/schema");

@BeforeClass
public void setupDistributions() throws IOException {
TestUtil.cleanDistribution();
}

@Test(description = "Tests with record field has constraint value with string type with unsupported patterns.")
public void constraintWithUnsupportedStringPattern() throws IOException, InterruptedException {
String openapiFilePath = "unsupported_string_pattern.yaml";
List<String> buildArgs = new LinkedList<>();
buildArgs.add(0, "openapi");
buildArgs.add("-i");
buildArgs.add(openapiFilePath);
buildArgs.add("--mode");
buildArgs.add("client");
buildArgs.add("-o");
buildArgs.add(tmpDir.toString());

String balFile = "bal";

if (System.getProperty("os.name").startsWith("Windows")) {
balFile = "bal.bat";
}
buildArgs.add(0, TEST_DISTRIBUTION_PATH.resolve(DISTRIBUTION_FILE_NAME).resolve("bin")
.resolve(balFile).toString());
OUT.println("Executing: " + StringUtils.join(buildArgs, ' '));
ProcessBuilder pb = new ProcessBuilder(buildArgs);
pb.directory(TEST_RESOURCE.toFile());
Process process = pb.start();

String out = "WARNING: ballerina can not support pattern: ^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$)) \n" +
"WARNING: ballerina can not support pattern: ^(?![0-9]+$)(?!-)[a-zA-Z0-9-]{2,49}[a-zA-Z0-9]$ \n" +
"WARNING: ballerina can not support pattern: (https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$ \n" +
"WARNING: ballerina can not support pattern: ^[A-Za-z\\-\\_\\/]+$ \n" +
"WARNING: ballerina can not support pattern: ^.*(?=.{6,1000})(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).*$ \n" +
"WARNING: ballerina can not support pattern: ^[\\x09\\x0A\\x0D\\x20\\x23\\x2D\\x30-\\x39\\x40-\\x5A\\x5E-\\x5F\\x61-\\x7A\\x7E-\\uD7FF\\uE000-\\uFFFD\\u10000-\\u10FFFF]{1,100}$ ";
//Thread for wait out put generate
Thread.sleep(5000);
// compare generated file has not included constraint annotation for scenario record field.
compareGeneratedSyntaxTreewithExpectedSyntaxTree("types.bal", "schema/unsupported_string_pattern.bal");
process.waitFor();
assertOnErrorStream(process, out);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import ballerina/http;
import ballerina/constraint;

# Provides a set of configurations for controlling the behaviours when communicating with a remote HTTP endpoint.
@display {label: "Connection Config"}
public type ConnectionConfig record {|
# The HTTP version understood by the client
http:HttpVersion httpVersion = http:HTTP_2_0;
# Configurations related to HTTP/1.x protocol
ClientHttp1Settings http1Settings?;
# Configurations related to HTTP/2 protocol
http:ClientHttp2Settings http2Settings?;
# The maximum time to wait (in seconds) for a response before closing the connection
decimal timeout = 60;
# The choice of setting `forwarded`/`x-forwarded` header
string forwarded = "disable";
# Configurations associated with request pooling
http:PoolConfiguration poolConfig?;
# HTTP caching related configurations
http:CacheConfig cache?;
# Specifies the way of handling compression (`accept-encoding`) header
http:Compression compression = http:COMPRESSION_AUTO;
# Configurations associated with the behaviour of the Circuit Breaker
http:CircuitBreakerConfig circuitBreaker?;
# Configurations associated with retrying
http:RetryConfig retryConfig?;
# Configurations associated with inbound response size limits
http:ResponseLimitConfigs responseLimits?;
# SSL/TLS-related options
http:ClientSecureSocket secureSocket?;
# Proxy server related options
http:ProxyConfig proxy?;
# Enables the inbound payload validation functionality which provided by the constraint package. Enabled by default
boolean validation = true;
|};

# Provides settings related to HTTP/1.x protocol.
public type ClientHttp1Settings record {|
# Specifies whether to reuse a connection for multiple requests
http:KeepAlive keepAlive = http:KEEPALIVE_AUTO;
# The chunking behaviour of the request
http:Chunking chunking = http:CHUNKING_AUTO;
# Proxy server related options
ProxyConfig proxy?;
|};

# Proxy server configurations to be used with the HTTP client endpoint.
public type ProxyConfig record {|
# Host name of the proxy server
string host = "";
# Proxy server port
int port = 0;
# Proxy server username
string userName = "";
# Proxy server password
@display {label: "", kind: "password"}
string password = "";
|};

public type PersonHobbyItemsString string;

public type Person record {
@constraint:String {maxLength: 14}
string name?;
PersonHobbyItemsString[] hobby?;
string url?;
string street?;
string net?;
string doi?;
string task?;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
openapi: 3.0.1
info:
title: Service Openapi Yaml
version: 2.0.0
servers:
- url: "{server}:{port}/"
variables:
server:
default: http://localhost
port:
default: "9090"
paths:
/greeting:
put:
summary: A resource for generating greetings
operationId: getGreeting
parameters:
- name: name
in: query
description: the input string name
required: true
schema:
type: string
requestBody:
content:
text/plain:
schema:
$ref: "#/components/schemas/Person"
responses:
"200":
description: Ok
components:
schemas:
Person:
type: object
required:
- id
properties:
name:
type: string
pattern: ^(?!(.*[\"\*\\\>\<\?\/\:\|]+.*)|(.*[\.]?.*[\.]+$)|(.*[ ]+$))
maxLength: 14
hobby:
type: array
items:
type: string
pattern: ^(?![0-9]+$)(?!-)[a-zA-Z0-9-]{2,49}[a-zA-Z0-9]$
url:
type: string
pattern: (https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$
street:
type: string
pattern: ^[A-Za-z\-\_\/]+$
net:
type: string
pattern: ^.*(?=.{6,1000})(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).*$
doi:
type: string
pattern: ^[\x09\x0A\x0D\x20\x23\x2D\x30-\x39\x40-\x5A\x5E-\x5F\x61-\x7A\x7E-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]{1,100}$
task:
type: string
pattern: ^[a-z0-9\-\_\.]+$
1 change: 1 addition & 0 deletions openapi-integration-tests/src/test/resources/testng.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<class name="io.ballerina.openapi.cmd.ServiceGeneratorReturnTypeTests"/>
<class name="io.ballerina.openapi.cmd.ClientGenerationTests"/>
<class name="io.ballerina.openapi.cmd.SchemaGenerationTests"/>
<class name="io.ballerina.openapi.cmd.SchemaGenerationNegativeTests"/>
<!-- <class name="io.ballerina.openapi.idl.client.IDLClientGenPluginTests"/>-->
<!-- <class name="io.ballerina.openapi.idl.client.IDLClientGenPluginNegativeTests"/>-->
</classes>
Expand Down

0 comments on commit 0ee787e

Please sign in to comment.