Move off deprecations.
This commit is contained in:
@@ -341,7 +341,7 @@ public class ClientHttpRequestFactoryFactory {
|
||||
SslConfiguration sslConfiguration) throws GeneralSecurityException,
|
||||
IOException {
|
||||
|
||||
final Netty4ClientHttpRequestFactory requestFactory = new Netty4ClientHttpRequestFactory();
|
||||
Netty4ClientHttpRequestFactory requestFactory = new Netty4ClientHttpRequestFactory();
|
||||
|
||||
if (hasSslConfiguration(sslConfiguration)) {
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ import org.springframework.vault.authentication.KubernetesServiceAccountTokenFil
|
||||
import org.springframework.vault.authentication.MacAddressUserId;
|
||||
import org.springframework.vault.authentication.StaticUserId;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.RoleId;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.SecretId;
|
||||
import org.springframework.vault.authentication.AwsEc2AuthenticationOptions.AwsEc2AuthenticationOptionsBuilder;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
@@ -286,10 +288,10 @@ public class EnvironmentVaultConfiguration extends AbstractVaultConfiguration im
|
||||
"Vault AppRole authentication: RoleId (vault.app-role.role-id) must not be empty");
|
||||
|
||||
AppRoleAuthenticationOptions.AppRoleAuthenticationOptionsBuilder builder = AppRoleAuthenticationOptions
|
||||
.builder().roleId(roleId);
|
||||
.builder().roleId(RoleId.provided(roleId));
|
||||
|
||||
if (StringUtils.hasText(secretId)) {
|
||||
builder = builder.secretId(secretId);
|
||||
builder = builder.secretId(SecretId.provided(secretId));
|
||||
}
|
||||
|
||||
return new AppRoleAuthentication(builder.build(), restOperations());
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.vault.core.lease;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -117,8 +118,9 @@ public enum LeaseEndpoints {
|
||||
Number leaseDuration = (Number) body.get("lease_duration");
|
||||
boolean renewable = (Boolean) body.get("renewable");
|
||||
|
||||
return Lease.of(leaseId, leaseDuration != null ? leaseDuration.longValue() : 0,
|
||||
renewable);
|
||||
return Lease
|
||||
.of(leaseId, Duration.ofSeconds(leaseDuration != null ? leaseDuration
|
||||
.longValue() : 0), renewable);
|
||||
}
|
||||
|
||||
private static HttpEntity<Object> getLeaseRenewalBody(Lease lease) {
|
||||
|
||||
@@ -43,7 +43,7 @@ class AppRoleAuthenticationIntegrationTests extends
|
||||
|
||||
String roleId = getRoleId("no-secret-id");
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId(roleId).build();
|
||||
.roleId(RoleId.provided(roleId)).build();
|
||||
AppRoleAuthentication authentication = new AppRoleAuthentication(options,
|
||||
prepare().getRestTemplate());
|
||||
|
||||
@@ -54,7 +54,8 @@ class AppRoleAuthenticationIntegrationTests extends
|
||||
void shouldAuthenticateWithFullPullMode() {
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.appRole("with-secret-id").initialToken(Settings.token()).build();
|
||||
.appRole("with-secret-id").roleId(RoleId.pull(Settings.token()))
|
||||
.secretId(SecretId.pull(Settings.token())).build();
|
||||
AppRoleAuthentication authentication = new AppRoleAuthentication(options,
|
||||
prepare().getRestTemplate());
|
||||
|
||||
@@ -65,8 +66,9 @@ class AppRoleAuthenticationIntegrationTests extends
|
||||
void shouldAuthenticateWithPullMode() {
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId(getRoleId("with-secret-id")).appRole("with-secret-id")
|
||||
.initialToken(Settings.token()).build();
|
||||
.roleId(RoleId.provided(getRoleId("with-secret-id")))
|
||||
.appRole("with-secret-id").secretId(SecretId.pull(Settings.token()))
|
||||
.build();
|
||||
AppRoleAuthentication authentication = new AppRoleAuthentication(options,
|
||||
prepare().getRestTemplate());
|
||||
|
||||
@@ -82,7 +84,8 @@ class AppRoleAuthenticationIntegrationTests extends
|
||||
null).getRequiredData().get("secret_id");
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId(roleId).secretId(secretId).build();
|
||||
.roleId(RoleId.provided(roleId)).secretId(SecretId.provided(secretId))
|
||||
.build();
|
||||
AppRoleAuthentication authentication = new AppRoleAuthentication(options,
|
||||
prepare().getRestTemplate());
|
||||
|
||||
@@ -129,7 +132,7 @@ class AppRoleAuthenticationIntegrationTests extends
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.secretId(SecretId.wrapped(VaultToken.of(unwrappingToken)))
|
||||
.roleId(roleId).build();
|
||||
.roleId(RoleId.provided(roleId)).build();
|
||||
|
||||
AppRoleAuthentication authentication = new AppRoleAuthentication(options,
|
||||
prepare().getRestTemplate());
|
||||
@@ -143,7 +146,7 @@ class AppRoleAuthenticationIntegrationTests extends
|
||||
String roleId = getRoleId("with-secret-id");
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId(roleId).build();
|
||||
.roleId(RoleId.provided(roleId)).build();
|
||||
AppRoleAuthentication authentication = new AppRoleAuthentication(options,
|
||||
prepare().getRestTemplate());
|
||||
|
||||
@@ -156,7 +159,8 @@ class AppRoleAuthenticationIntegrationTests extends
|
||||
String roleId = getRoleId("with-secret-id");
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId(roleId).secretId("this-is-a-wrong-secret-id").build();
|
||||
.roleId(RoleId.provided(roleId))
|
||||
.secretId(SecretId.provided("this-is-a-wrong-secret-id")).build();
|
||||
AppRoleAuthentication authentication = new AppRoleAuthentication(options,
|
||||
prepare().getRestTemplate());
|
||||
|
||||
@@ -174,7 +178,8 @@ class AppRoleAuthenticationIntegrationTests extends
|
||||
Collections.singletonMap("secret_id", secretId));
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId(roleId).secretId(secretId).build();
|
||||
.roleId(RoleId.provided(roleId)).secretId(SecretId.provided(secretId))
|
||||
.build();
|
||||
AppRoleAuthentication authentication = new AppRoleAuthentication(options,
|
||||
prepare().getRestTemplate());
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ class AppRoleAuthenticationStepsIntegrationTests extends
|
||||
VaultToken unwrappingToken = generateWrappedSecretIdResponse();
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.secretId(SecretId.wrapped(unwrappingToken)).roleId(roleId).build();
|
||||
.secretId(SecretId.wrapped(unwrappingToken))
|
||||
.roleId(RoleId.provided(roleId)).build();
|
||||
|
||||
AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(
|
||||
AppRoleAuthentication.createAuthenticationSteps(options), prepare()
|
||||
@@ -79,7 +80,8 @@ class AppRoleAuthenticationStepsIntegrationTests extends
|
||||
void shouldAuthenticateWithFullPullMode() {
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.appRole("with-secret-id").initialToken(Settings.token()).build();
|
||||
.appRole("with-secret-id").roleId(RoleId.pull(Settings.token()))
|
||||
.secretId(SecretId.pull(Settings.token())).build();
|
||||
|
||||
AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(
|
||||
AppRoleAuthentication.createAuthenticationSteps(options), prepare()
|
||||
@@ -95,7 +97,7 @@ class AppRoleAuthenticationStepsIntegrationTests extends
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.appRole("with-secret-id").secretId(SecretId.pull(Settings.token()))
|
||||
.roleId(roleId).build();
|
||||
.roleId(RoleId.provided(roleId)).build();
|
||||
|
||||
AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(
|
||||
AppRoleAuthentication.createAuthenticationSteps(options), prepare()
|
||||
@@ -128,7 +130,8 @@ class AppRoleAuthenticationStepsIntegrationTests extends
|
||||
String roleId = getRoleId("with-secret-id");
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId(roleId).secretId("this-is-a-wrong-secret-id").build();
|
||||
.roleId(RoleId.provided(roleId))
|
||||
.secretId(SecretId.provided("this-is-a-wrong-secret-id")).build();
|
||||
|
||||
AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(
|
||||
AppRoleAuthentication.createAuthenticationSteps(options), prepare()
|
||||
@@ -148,7 +151,8 @@ class AppRoleAuthenticationStepsIntegrationTests extends
|
||||
Collections.singletonMap("secret_id", secretId));
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId(roleId).secretId(secretId).build();
|
||||
.roleId(RoleId.provided(roleId)).secretId(SecretId.provided(secretId))
|
||||
.build();
|
||||
|
||||
AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(
|
||||
AppRoleAuthentication.createAuthenticationSteps(options), prepare()
|
||||
@@ -167,8 +171,8 @@ class AppRoleAuthenticationStepsIntegrationTests extends
|
||||
String roleId = getRoleId("with-secret-id");
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId(roleId).appRole("with-secret-id").initialToken(Settings.token())
|
||||
.build();
|
||||
.roleId(RoleId.provided(roleId)).appRole("with-secret-id")
|
||||
.secretId(SecretId.pull(Settings.token())).build();
|
||||
|
||||
AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(
|
||||
AppRoleAuthentication.createAuthenticationSteps(options), prepare()
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.RoleId;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.SecretId;
|
||||
import org.springframework.vault.client.VaultClients;
|
||||
import org.springframework.vault.client.VaultClients.PrefixAwareUriTemplateHandler;
|
||||
@@ -71,8 +72,8 @@ class AppRoleAuthenticationUnitTests {
|
||||
void loginShouldObtainToken() {
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId("hello") //
|
||||
.secretId("world") //
|
||||
.roleId(RoleId.provided("hello")) //
|
||||
.secretId(SecretId.provided("world")) //
|
||||
.build();
|
||||
|
||||
mockRest.expect(requestTo("/auth/approle/login"))
|
||||
@@ -150,7 +151,7 @@ class AppRoleAuthenticationUnitTests {
|
||||
void loginShouldObtainTokenWithoutSecretId() {
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId("hello") //
|
||||
.roleId(RoleId.provided("hello")) //
|
||||
.build();
|
||||
|
||||
mockRest.expect(requestTo("/auth/approle/login"))
|
||||
@@ -179,7 +180,7 @@ class AppRoleAuthenticationUnitTests {
|
||||
void loginShouldFail() {
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId("hello") //
|
||||
.roleId(RoleId.provided("hello")) //
|
||||
.build();
|
||||
|
||||
mockRest.expect(requestTo("/auth/approle/login")) //
|
||||
@@ -193,7 +194,7 @@ class AppRoleAuthenticationUnitTests {
|
||||
void loginShouldUnwrapSecretIdResponse() throws Exception {
|
||||
|
||||
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
|
||||
.roleId("my_role_id")
|
||||
.roleId(RoleId.provided("my_role_id"))
|
||||
.secretId(SecretId.wrapped(VaultToken.of("unwrapping_token"))).build();
|
||||
|
||||
String wrappedResponse = "{"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.vault.authentication;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -80,7 +81,7 @@ class LifecycleAwareSessionManagerIntegrationTests extends IntegrationTestSuppor
|
||||
VaultToken token = tokenOperations.create(tokenRequest).getToken();
|
||||
|
||||
TokenAuthentication tokenAuthentication = new TokenAuthentication(
|
||||
LoginToken.renewable(token.getToken(), 0));
|
||||
LoginToken.renewable(token.getToken().toCharArray(), Duration.ZERO));
|
||||
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
LifecycleAwareSessionManager sessionManager = new LifecycleAwareSessionManager(
|
||||
|
||||
@@ -186,6 +186,7 @@ class LifecycleAwareSessionManagerUnitTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void shouldRevokeLoginTokenOnDestroy() {
|
||||
|
||||
when(clientAuthentication.login()).thenReturn(LoginToken.of("login"));
|
||||
@@ -217,6 +218,7 @@ class LifecycleAwareSessionManagerUnitTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void shouldNotThrowExceptionsOnRevokeErrors() {
|
||||
|
||||
when(clientAuthentication.login()).thenReturn(LoginToken.of("login"));
|
||||
@@ -252,6 +254,7 @@ class LifecycleAwareSessionManagerUnitTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void shouldRunTokenRenewal() {
|
||||
|
||||
when(clientAuthentication.login()).thenReturn(
|
||||
@@ -270,8 +273,9 @@ class LifecycleAwareSessionManagerUnitTests {
|
||||
|
||||
verify(restOperations).postForObject(
|
||||
eq("auth/token/renew-self"),
|
||||
eq(new HttpEntity<Object>(VaultHttpHeaders.from(LoginToken.renewable(
|
||||
"login", 5)))), any(Class.class));
|
||||
eq(new HttpEntity<>(VaultHttpHeaders.from(LoginToken.renewable(
|
||||
"login".toCharArray(), Duration.ofSeconds(5))))),
|
||||
any(Class.class));
|
||||
verify(clientAuthentication, times(1)).login();
|
||||
verify(listener).onAuthenticationEvent(any(BeforeLoginTokenRenewedEvent.class));
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginTokenRenewedEvent.class));
|
||||
@@ -380,6 +384,7 @@ class LifecycleAwareSessionManagerUnitTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void shouldNotReScheduleTokenRenewalAfterFailedRenewal() {
|
||||
|
||||
when(clientAuthentication.login()).thenReturn(
|
||||
@@ -408,19 +413,20 @@ class LifecycleAwareSessionManagerUnitTests {
|
||||
sessionManager.renewToken();
|
||||
|
||||
assertThat(sessionManager.getSessionToken()).isEqualTo(
|
||||
LoginToken.renewable("login", 5));
|
||||
LoginToken.renewable("login".toCharArray(), Duration.ofSeconds(5)));
|
||||
verify(clientAuthentication, times(1)).login();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void renewShouldReportFalseIfTokenRenewalFails() {
|
||||
|
||||
when(clientAuthentication.login()).thenReturn(
|
||||
LoginToken.renewable("login".toCharArray(), Duration.ofSeconds(5)));
|
||||
when(
|
||||
restOperations.postForObject(anyString(),
|
||||
ArgumentMatchers.<Object> any(), ArgumentMatchers.<Class> any()))
|
||||
.thenThrow(new HttpServerErrorException(HttpStatus.BAD_REQUEST));
|
||||
restOperations.postForObject(anyString(), ArgumentMatchers.any(),
|
||||
ArgumentMatchers.<Class> any())).thenThrow(
|
||||
new HttpServerErrorException(HttpStatus.BAD_REQUEST));
|
||||
|
||||
sessionManager.getSessionToken();
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package org.springframework.vault.core;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -71,8 +71,8 @@ class VaultTokenTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
VaultTokenRequest tokenRequest = VaultTokenRequest.builder()
|
||||
.displayName("display") //
|
||||
.explicitMaxTtl(TimeUnit.HOURS.toSeconds(10)) //
|
||||
.ttl(30 * 60) //
|
||||
.explicitMaxTtl(Duration.ofHours(5)) //
|
||||
.ttl(Duration.ofMinutes(30 * 60)) //
|
||||
.policies(Collections.singleton("root")) //
|
||||
.numUses(2) //
|
||||
.renewable() //
|
||||
@@ -98,8 +98,8 @@ class VaultTokenTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
VaultTokenRequest tokenRequest = VaultTokenRequest.builder()
|
||||
.displayName("display") //
|
||||
.explicitMaxTtl(TimeUnit.HOURS.toSeconds(10)) //
|
||||
.ttl(30 * 60) //
|
||||
.explicitMaxTtl(Duration.ofHours(5)) //
|
||||
.ttl(Duration.ofMinutes(30 * 60)) //
|
||||
.policies(Collections.singleton("root")) //
|
||||
.numUses(2) //
|
||||
.renewable() //
|
||||
@@ -117,8 +117,8 @@ class VaultTokenTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
void renewShouldRenewToken() {
|
||||
|
||||
VaultTokenRequest tokenRequest = VaultTokenRequest.builder()
|
||||
.explicitMaxTtl(TimeUnit.HOURS.toSeconds(10)) //
|
||||
.ttl(30 * 60) //
|
||||
.explicitMaxTtl(Duration.ofHours(5)) //
|
||||
.ttl(Duration.ofMinutes(30 * 60)) //
|
||||
.renewable() //
|
||||
.build();
|
||||
|
||||
@@ -161,6 +161,7 @@ class VaultTokenTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private ResponseEntity<String> lookupSelf(final VaultTokenResponse tokenResponse) {
|
||||
|
||||
return vaultOperations
|
||||
@@ -169,9 +170,8 @@ class VaultTokenTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
headers.add(VaultHttpHeaders.VAULT_TOKEN, tokenResponse.getToken()
|
||||
.getToken());
|
||||
|
||||
return restOperations
|
||||
.exchange("auth/token/lookup-self", HttpMethod.GET,
|
||||
new HttpEntity<Object>(headers), String.class);
|
||||
return restOperations.exchange("auth/token/lookup-self",
|
||||
HttpMethod.GET, new HttpEntity<>(headers), String.class);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ import org.springframework.vault.support.VaultResponse;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -209,6 +209,7 @@ class SecretLeaseContainerUnitTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void shouldRenewLease() {
|
||||
|
||||
prepareRenewal();
|
||||
@@ -563,13 +564,13 @@ class SecretLeaseContainerUnitTests {
|
||||
secrets.setLeaseId("lease");
|
||||
secrets.setRenewable(renewable);
|
||||
secrets.setLeaseDuration(100);
|
||||
secrets.setData(Collections.singletonMap(key, (Object) value));
|
||||
secrets.setData(Collections.singletonMap(key, value));
|
||||
|
||||
return secrets;
|
||||
}
|
||||
|
||||
private VaultResponse createGenericSecrets() {
|
||||
return createGenericSecrets(Collections.singletonMap("key", (Object) "value"));
|
||||
return createGenericSecrets(Collections.singletonMap("key", "value"));
|
||||
}
|
||||
|
||||
private VaultResponse createGenericSecrets(Map<String, Object> data) {
|
||||
|
||||
@@ -49,8 +49,9 @@ public class Version implements Comparable<Version> {
|
||||
*/
|
||||
private Version(int... parts) {
|
||||
|
||||
Assert.notNull(parts);
|
||||
Assert.isTrue(parts.length > 0 && parts.length < 5);
|
||||
Assert.notNull(parts, "Parts must not be null");
|
||||
Assert.isTrue(parts.length > 0 && parts.length < 5,
|
||||
"Parts must contain one to four segments");
|
||||
|
||||
this.major = parts[0];
|
||||
this.minor = parts.length > 1 ? parts[1] : 0;
|
||||
|
||||
Reference in New Issue
Block a user