Commit 7bf42e8c authored by Mina Otgonbold's avatar Mina Otgonbold
Browse files

updated core common version

parents 8e3cee69 3c7fdb22
Pipeline #32959 failed with stages
in 45 seconds
......@@ -32,10 +32,18 @@ analyze:
type: mvn
target: provider/storage-aws/pom.xml
path: .
- name: storage-aws-mongodb
type: mvn
target: provider/storage-aws-mongodb/pom.xml
path: .
- name: storage-ibm
type: mvn
target: provider/storage-ibm/pom.xml
path: .
- name: storage-reference
type: mvn
target: provider/storage-reference/pom.xml
path: .
- name: Compliance Trigger Function
type: mvn
target: pom.xml
......
......@@ -443,6 +443,7 @@ The following software have components provided under the terms of this license:
- Asynchronous Http Client Netty Utils (from )
- Azure AD Spring Security Integration Spring Boot Starter (from https://github.com/Microsoft/azure-spring-boot)
- Azure Metrics Spring Boot Starter (from https://github.com/Microsoft/azure-spring-boot)
- BSON (from http://bsonspec.org)
- Bean Validation API (from http://beanvalidation.org)
- Byte Buddy (without dependencies) (from )
- Byte Buddy Java agent (from )
......@@ -585,9 +586,12 @@ The following software have components provided under the terms of this license:
- Microsoft Azure Netty HTTP Client Library (from https://github.com/Azure/azure-sdk-for-java)
- Microsoft Azure SDK for SQL API of Azure Cosmos DB Service (from https://github.com/Azure/azure-sdk-for-java)
- Mockito (from http://www.mockito.org)
- Mockito (from http://www.mockito.org)
- Mockito (from http://mockito.org)
- Mockito (from http://mockito.org)
- Mockito (from http://www.mockito.org)
- MongoDB Driver (from http://www.mongodb.org)
- MongoDB Java Driver (from http://www.mongodb.org)
- MongoDB Java Driver Core (from http://www.mongodb.org)
- Netty Reactive Streams HTTP support (from )
- Netty Reactive Streams Implementation (from )
- Netty/Buffer (from http://netty.io/)
......@@ -619,11 +623,13 @@ The following software have components provided under the terms of this license:
- Objenesis (from http://objenesis.org)
- OkHttp (from )
- OkHttp (from )
- OkHttp (from )
- OkHttp Logging Interceptor (from )
- OkHttp URLConnection (from )
- OkHttp URLConnection (from )
- Okio (from )
- Okio (from )
- Okio (from )
- OpenCensus (from https://github.com/census-instrumentation/opencensus-java)
- OpenCensus (from https://github.com/census-instrumentation/opencensus-java)
- OpenCensus (from https://github.com/census-instrumentation/opencensus-java)
......@@ -631,9 +637,11 @@ The following software have components provided under the terms of this license:
- PowerMock (from http://www.powermock.org)
- Protocol Buffer extensions to the Google HTTP Client Library for Java. (from )
- QpidJMS Client (from )
- RabbitMQ Java Client (from http://www.rabbitmq.com)
- Reactive Streams Netty driver (from https://github.com/reactor/reactor-netty)
- Retrofit (from )
- Simple XML (from http://simple.sourceforge.net)
- Simple XML (safe) (from https://github.com/dweiss/simplexml)
- SnakeYAML (from http://www.snakeyaml.org)
- SnakeYAML (from http://www.snakeyaml.org)
- Spring AOP (from https://github.com/spring-projects/spring-framework)
......@@ -646,6 +654,7 @@ The following software have components provided under the terms of this license:
- Spring Boot Actuator AutoConfigure (from https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-actuator-autoconfigure)
- Spring Boot AutoConfigure (from http://projects.spring.io/spring-boot/)
- Spring Boot AutoConfigure (from http://projects.spring.io/spring-boot/)
- Spring Boot Data MongoDB Starter (from http://projects.spring.io/spring-boot/)
- Spring Boot Dependencies (from http://projects.spring.io/spring-boot/)
- Spring Boot Json Starter (from https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-json)
- Spring Boot Json Starter (from https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-json)
......@@ -672,6 +681,7 @@ The following software have components provided under the terms of this license:
- Spring Core (from https://github.com/spring-projects/spring-framework)
- Spring Data Core (from )
- Spring Data Core (from )
- Spring Data MongoDB - Core (from )
- Spring Expression Language (SpEL) (from https://github.com/spring-projects/spring-framework)
- Spring Expression Language (SpEL) (from https://github.com/spring-projects/spring-framework)
- Spring JMS (from https://github.com/spring-projects/spring-framework)
......@@ -732,6 +742,7 @@ The following software have components provided under the terms of this license:
- lettuce (from http://github.com/mp911de/lettuce/wiki)
- micrometer-core (from https://github.com/micrometer-metrics/micrometer)
- micrometer-registry-azure-monitor (from https://github.com/micrometer-metrics/micrometer)
- minio (from https://github.com/minio/minio-java)
- org.apiguardian:apiguardian-api (from https://github.com/apiguardian-team/apiguardian)
- org.opentest4j:opentest4j (from https://github.com/ota4j-team/opentest4j)
- org.xmlunit:xmlunit-core (from http://www.xmlunit.org/)
......@@ -850,8 +861,11 @@ CC-BY-2.5
========================================================================
The following software have components provided under the terms of this license:
- "Java Concurrency in Practice" book annotations (from http://jcip.net/)
- Checker Qual (from https://checkerframework.org)
- FindBugs-jsr305 (from http://findbugs.sourceforge.net/)
- MongoDB Java Driver (from http://www.mongodb.org)
- MongoDB Java Driver Core (from http://www.mongodb.org)
========================================================================
CC-BY-4.0
......@@ -952,6 +966,7 @@ The following software have components provided under the terms of this license:
- JavaBeans Activation Framework (from )
- JavaMail API (from )
- OSGi resource locator (from )
- RabbitMQ Java Client (from http://www.rabbitmq.com)
- ServiceLocator Default Implementation (from git://java.net/hk2~git/hk2-locator)
- aopalliance-repackaged (from )
- javax.annotation-api (from http://jcp.org/en/jsr/detail?id=250)
......@@ -981,6 +996,7 @@ The following software have components provided under the terms of this license:
- JavaBeans Activation Framework (from )
- JavaMail API (from )
- OSGi resource locator (from )
- RabbitMQ Java Client (from http://www.rabbitmq.com)
- javax.annotation-api (from http://jcp.org/en/jsr/detail?id=250)
- javax.ws.rs-api (from http://jax-rs-spec.java.net)
- javax.ws.rs-api (from http://jax-rs-spec.java.net)
......@@ -1026,6 +1042,7 @@ The following software have components provided under the terms of this license:
- Microsoft Application Insights Java SDK Spring Boot starter (from https://github.com/Microsoft/ApplicationInsights-Java)
- Microsoft Application Insights Java SDK Web Module (from https://github.com/Microsoft/ApplicationInsights-Java)
- Microsoft Application Insights Log4j 2 Appender (from https://github.com/Microsoft/ApplicationInsights-Java)
- SpotBugs Annotations (from https://spotbugs.github.io/)
========================================================================
LGPL-2.1-or-later
......@@ -1036,6 +1053,7 @@ The following software have components provided under the terms of this license:
- Javassist (from http://www.javassist.org/)
- SnakeYAML (from http://www.snakeyaml.org)
- SnakeYAML (from http://www.snakeyaml.org)
- SpotBugs Annotations (from https://spotbugs.github.io/)
========================================================================
LGPL-3.0-only
......@@ -1045,6 +1063,7 @@ The following software have components provided under the terms of this license:
- Apache Log4j API (from )
- Apache Log4j API (from )
- Apache Log4j Core (from )
- RabbitMQ Java Client (from http://www.rabbitmq.com)
========================================================================
MIT
......@@ -1125,6 +1144,7 @@ The following software have components provided under the terms of this license:
- Javassist (from http://www.javassist.org/)
- Javassist (from http://www.javassist.org/)
- Javassist (from http://www.javassist.org/)
- RabbitMQ Java Client (from http://www.rabbitmq.com)
========================================================================
MPL-2.0
......@@ -1134,6 +1154,8 @@ The following software have components provided under the terms of this license:
- Javassist (from http://www.javassist.org/)
- Javassist (from http://www.javassist.org/)
- Javassist (from http://www.javassist.org/)
- OkHttp (from )
- RabbitMQ Java Client (from http://www.rabbitmq.com)
========================================================================
PHP-3.01
......@@ -1190,6 +1212,7 @@ The following software have components provided under the terms of this license:
- Microsoft Azure client library for Blob Storage (from https://github.com/Azure/azure-sdk-for-java)
- Project Lombok (from https://projectlombok.org)
- Project Lombok (from https://projectlombok.org)
- RabbitMQ Java Client (from http://www.rabbitmq.com)
- Spring Web (from https://github.com/spring-projects/spring-framework)
- Spring Web (from https://github.com/spring-projects/spring-framework)
- StAX API (from http://stax.codehaus.org/)
......@@ -1202,10 +1225,12 @@ unknown
========================================================================
The following software have components provided under the terms of this license:
- "Java Concurrency in Practice" book annotations (from http://jcip.net/)
- Byte Buddy (without dependencies) (from )
- JUnit (from http://junit.org)
- JUnit Jupiter (Aggregator) (from https://junit.org/junit5/)
- JavaBeans Activation Framework API jar (from )
- RabbitMQ Java Client (from http://www.rabbitmq.com)
- Spongy Castle (from http://rtyley.github.io/spongycastle/)
- jakarta.xml.bind-api (from )
- org.junit.jupiter:junit-jupiter-api (from http://junit.org/junit5/)
......
......@@ -100,7 +100,7 @@ spec:
- name: servicebus_topic_name
value: recordstopic
- name: entitlements_service_endpoint
value: http://entitlements-azure/entitlements/v1
value: http://entitlements/api/entitlements/v2
- name: entitlements_service_api_key
value: "OBSOLETE"
- name: legal_service_endpoint
......
......@@ -64,6 +64,10 @@ az keyvault secret show --vault-name $KEY_VAULT_NAME --name $KEY_VAULT_SECRET_NA
| `BULK_EXECUTOR_MAX_RUS` | `4000` | Maximum RU Consumption for bulk uploads in each storage service pod. NOTE: This is an attempted maximum and it may exceed this number. | no | Recommended to set to 4000, but can be changed for specific use cases. NOTE: If not set, this will default to 4000.|
| `DOCUMENT_CLIENT_MAX_POOL_SIZE` | See description | Connection pool size for bulk upload http client. Recommended to not set and allow for default value which is calculated based on the number of available processors. | no | Recommended to not set manually, but can be changed for specific use cases. NOTE: If not set, this will default to 100 * number of available processors.|
**Run the service in intellij**
Add VM option `-Dspring.profiles.active=local` in the Edit Configurations Section to activate `application-local.properties` that will avoid unnecessary changes to
`application.properties`.
**Required to run integration tests**
......
......@@ -33,8 +33,13 @@
<azure.appservice.plan />
<azure.appservice.appname />
<azure.appservice.subscription />
<<<<<<< HEAD
<osdu.corelibazure.version>0.0.63</osdu.corelibazure.version>
<osdu.oscorecommon.version>0.8.1</osdu.oscorecommon.version>
=======
<osdu.corelibazure.version>0.6.1</osdu.corelibazure.version>
<osdu.oscorecommon.version>0.6.9</osdu.oscorecommon.version>
>>>>>>> 3c7fdb22a0afc9288951daf42e3e4c1d9bbc9b67
<osdu.storage-core.version>0.8.0-SNAPSHOT</osdu.storage-core.version>
<junit.version>4.12</junit.version>
<mockito.version>1.10.19</mockito.version>
......@@ -95,7 +100,6 @@
<dependency>
<groupId>org.opengroup.osdu</groupId>
<artifactId>os-core-common</artifactId>
<version>${osdu.oscorecommon.version}</version>
<exclusions>
<exclusion>
<artifactId>elasticsearch</artifactId>
......
......@@ -20,7 +20,6 @@ import com.microsoft.azure.eventgrid.models.EventGridEvent;
import com.microsoft.azure.servicebus.Message;
import org.joda.time.DateTime;
import org.opengroup.osdu.azure.eventgrid.EventGridTopicStore;
import org.opengroup.osdu.azure.eventgrid.TopicName;
import org.opengroup.osdu.azure.servicebus.ITopicClientFactory;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.core.common.model.http.DpsHeaders;
......@@ -36,26 +35,21 @@ import java.util.*;
@Component
public class MessageBusImpl implements IMessageBus {
private final static String RECORDS_CHANGED_EVENT_SUBJECT = "RecordsChanged";
private final static String RECORDS_CHANGED_EVENT_TYPE = "RecordsChanged";
private final static String RECORDS_CHANGED_EVENT_DATA_VERSION = "1.0";
@Autowired
EventGridConfig eventGridConfig;
@Autowired
private ITopicClientFactory topicClientFactory;
@Autowired
private EventGridTopicStore eventGridTopicStore;
@Autowired
private JaxRsDpsLog logger;
@Autowired
@Named("SERVICE_BUS_TOPIC")
private String serviceBusTopic;
@Autowired
EventGridConfig eventGridConfig;
private final static String EVENT_SUBJECT = "RecordsChanged";
private final static String EVENT_TYPE = "RecordsChanged";
private final static String EVENT_DATA_VERSION = "1.0";
@Override
public void publishMessage(DpsHeaders headers, PubSubInfo... messages) {
publishToServiceBus(headers, messages);
......@@ -79,11 +73,11 @@ public class MessageBusImpl implements IMessageBus {
String messageId = UUID.randomUUID().toString();
eventsList.add(new EventGridEvent(
messageId,
EVENT_SUBJECT,
RECORDS_CHANGED_EVENT_SUBJECT,
data,
EVENT_TYPE,
RECORDS_CHANGED_EVENT_TYPE,
DateTime.now(),
EVENT_DATA_VERSION
RECORDS_CHANGED_EVENT_DATA_VERSION
));
logger.info("Event generated: " + messageId);
......@@ -93,7 +87,7 @@ public class MessageBusImpl implements IMessageBus {
// Event Grid has a capability to publish multiple events in an array. This will have perf implications,
// hence publishing one event at a time. If we are confident about the perf capabilities of consumer services,
// we can publish more more than one event in an array.
eventGridTopicStore.publishToEventGridTopic(headers.getPartitionId(), TopicName.RECORDS_CHANGED, eventsList);
eventGridTopicStore.publishToEventGridTopic(headers.getPartitionId(), eventGridConfig.getTopicName(), eventsList);
}
}
......
......@@ -20,19 +20,36 @@ import org.springframework.context.annotation.Configuration;
@Configuration
public class EventGridConfig {
private boolean publishToEventGridEnabled;
// The Event Grid Event can be a maximum of 1MB. The batch size manipulation will impact the costing.
// https://docs.microsoft.com/en-us/azure/event-grid/event-schema#:~:text=Event%20sources%20send%20events%20to,is%20limited%20to%201%20MB.
private Integer eventGridBatchSize;
private String topicName;
public EventGridConfig(@Value("#{new Boolean('${azure.publishToEventGrid}')}") boolean publish,
@Value("#{new Integer('${azure.eventGridBatchSize}')}") int batchSize,
@Value("${azure.eventGrid.topicName}") String topicName) {
if (publish) {
if ((topicName.isEmpty() || batchSize <= 0)) {
throw new RuntimeException("Missing EventGrid Configuration");
}
}
this.publishToEventGridEnabled = publish;
this.eventGridBatchSize = batchSize;
this.topicName = topicName;
}
public boolean isPublishingToEventGridEnabled() {
return publishToEventGridEnabled;
}
public String getTopicName() {
return topicName;
}
public int getEventGridBatchSize() {
return eventGridBatchSize;
}
@Value("#{new Boolean('${azure.publishToEventGrid:true}')}")
private boolean publishToEventGridEnabled;
// The Event Grid Event can be a maximum of 1MB. The batch size manipulation will impact the costing.
// https://docs.microsoft.com/en-us/azure/event-grid/event-schema#:~:text=Event%20sources%20send%20events%20to,is%20limited%20to%201%20MB.
@Value("#{new Integer('${azure.eventGridBatchSize:10}')}")
private Integer eventGridBatchSize;
}
# Copyright © Microsoft Corporation
#
# Licensed 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.
runtime.env.local=true
azure.activedirectory.client-id=${aad_client_id}
azure.activedirectory.AppIdUri=api://${azure.activedirectory.client-id}
azure.activedirectory.session-stateless=true
# Istio Auth Enabled
azure.istio.auth.enabled=false
......@@ -72,6 +72,7 @@ redis.database=${REDIS_DATABASE}
# Azure Event Grid Configuration
azure.publishToEventGrid=true
azure.eventGridBatchSize=10
azure.eventGrid.topicName=recordstopic
#Health checks
management.health.azure-key-vault.enabled=false
......
......@@ -26,7 +26,6 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opengroup.osdu.azure.eventgrid.EventGridTopicStore;
import org.opengroup.osdu.azure.eventgrid.TopicName;
import org.opengroup.osdu.azure.servicebus.ITopicClientFactory;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.core.common.model.http.DpsHeaders;
......@@ -95,13 +94,14 @@ public class MessageBusImplTest {
}
ArgumentCaptor<String> partitionNameCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<TopicName> topicNameArgumentCaptor = ArgumentCaptor.forClass(TopicName.class);
ArgumentCaptor<String> topicNameArgumentCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<List<EventGridEvent>> listEventGridEventArgumentCaptor = ArgumentCaptor.forClass(List.class);
doNothing().when(this.eventGridTopicStore).publishToEventGridTopic(
partitionNameCaptor.capture(), topicNameArgumentCaptor.capture(), listEventGridEventArgumentCaptor.capture()
);
when(this.eventGridConfig.isPublishingToEventGridEnabled()).thenReturn(true);
when(this.eventGridConfig.getEventGridBatchSize()).thenReturn(5);
when(this.eventGridConfig.getTopicName()).thenReturn("recordstopic");
// Act
sut.publishMessage(this.dpsHeaders, pubSubInfo);
......@@ -111,7 +111,7 @@ public class MessageBusImplTest {
// The number of events that are being published is verified here.
assertEquals(1, listEventGridEventArgumentCaptor.getValue().size());
assertEquals(topicNameArgumentCaptor.getValue(), TopicName.RECORDS_CHANGED);
assertEquals(topicNameArgumentCaptor.getValue(), "recordstopic");
assertEquals(partitionNameCaptor.getValue(), PARTITION_ID);
// Validate all records are preserved.
......
// Copyright © Microsoft Corporation
//
// Licensed 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 org.opengroup.osdu.storage.provider.azure.di;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(MockitoExtension.class)
public class EventGridConfigTest {
private static String VALID_TOPIC_NAME = "topicname";
private static String INVALID_TOPIC_NAME = "";
private static int VALID_BATCH_SIZE = 10;
private static int INVALID_BATCH_SIZE = 0;
@Test
public void configurationValidationTests() {
// Positive Case
EventGridConfig eventGridConfig = new EventGridConfig(true, VALID_BATCH_SIZE, VALID_TOPIC_NAME);
assertEquals(VALID_TOPIC_NAME, eventGridConfig.getTopicName());
// Negative Cases
RuntimeException runtimeException = Assertions.assertThrows(RuntimeException.class,
() -> new EventGridConfig(true, VALID_BATCH_SIZE, INVALID_TOPIC_NAME));
assertEquals("Missing EventGrid Configuration", runtimeException.getMessage());
runtimeException = Assertions.assertThrows(RuntimeException.class,
() -> new EventGridConfig(true, INVALID_BATCH_SIZE, "topicName"));
assertEquals("Missing EventGrid Configuration", runtimeException.getMessage());
}
}
......@@ -40,7 +40,7 @@
<dependency>
<groupId>org.opengroup.osdu</groupId>
<artifactId>core-lib-gcp</artifactId>
<version>0.6.1-SNAPSHOT</version>
<version>0.7.0</version>
</dependency>
<dependency>
......
......@@ -26,7 +26,7 @@
<packaging>jar</packaging>
<properties>
<os-core-lib-ibm.version>0.3.24-SNAPSHOT</os-core-lib-ibm.version>
<os-core-lib-ibm.version>0.7.0</os-core-lib-ibm.version>
<start-class>org.opengroup.osdu.storage.provider.ibm.app.StorageIBMApplication</start-class>
</properties>
......
......@@ -12,10 +12,12 @@ import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.apache.http.HttpStatus;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.core.common.model.storage.DatastoreQueryResult;
import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
import org.opengroup.osdu.core.ibm.auth.ServiceCredentials;
import org.opengroup.osdu.core.ibm.cloudant.IBMCloudantClientFactory;
import org.opengroup.osdu.storage.provider.interfaces.IQueryRepository;
......@@ -47,6 +49,9 @@ public class QueryRepositoryImpl implements IQueryRepository {
private Database dbSchema;
private Database dbRecords;
@Inject
private TenantInfo tenant;
@PostConstruct
public void init() throws MalformedURLException{
......@@ -62,6 +67,12 @@ public class QueryRepositoryImpl implements IQueryRepository {
@Override
public DatastoreQueryResult getAllKinds(Integer limit, String cursor) {
try {
tenant.getName();
} catch (Exception e) {
throw new AppException(HttpStatus.SC_UNAUTHORIZED, "not authorized", "not authorized");
}
DatastoreQueryResult result = new DatastoreQueryResult();
String initialId = validateCursor(cursor, dbSchema);
......
......@@ -24,6 +24,7 @@ public class RecordMetadataDoc extends RecordMetadata {
super.setAcl(recordMetadata.getAcl());
super.setLegal(recordMetadata.getLegal());
super.setAncestry(recordMetadata.getAncestry());
super.setTags(recordMetadata.getTags());
super.setModifyUser(recordMetadata.getModifyUser());
super.setModifyTime(recordMetadata.getModifyTime());
......@@ -46,6 +47,7 @@ public class RecordMetadataDoc extends RecordMetadata {
rm.setCreateTime(this.getCreateTime());
rm.setModifyUser(this.getModifyUser());
rm.setModifyTime(this.getModifyTime());
rm.setTags(this.getTags());
return rm;
}
......
......@@ -35,7 +35,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.main.basedir>${project.basedir}</project.main.basedir>
<MY_TENANT>opendes</MY_TENANT>
<os-core-lib-ibm.version>0.3.6-SNAPSHOT</os-core-lib-ibm.version>
<os-core-lib-ibm.version>0.7.0</os-core-lib-ibm.version>
</properties>
<dependencies>
<!-- Internal packages -->
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment