Commit 532d6cec by Suma Shivaprasad

ATLAS-376 UI: Use the Schema API of the backend to populate details for Schema…

ATLAS-376 UI: Use the Schema API of the backend to populate details for Schema tab (darshankumar89 via sumasai)
parent 323b9005
...@@ -155,7 +155,8 @@ module.exports = function(grunt) { ...@@ -155,7 +155,8 @@ module.exports = function(grunt) {
'hostnames': ['*'], 'hostnames': ['*'],
'routes': { 'routes': {
'/': distPath, '/': distPath,
'/api': 'http://162.212.133.190:21000/api' //'/api': 'http://162.249.6.39:21000/api'
'/api': 'http://ec2-52-25-142-7.us-west-2.compute.amazonaws.com:21000/api'
} }
}] }]
} }
......
...@@ -17,23 +17,46 @@ ...@@ -17,23 +17,46 @@
*/ */
'use strict'; 'use strict';
angular.module('dgc.details').controller('DetailsController', ['$window', '$scope', '$state', '$stateParams', 'DetailsResource', angular.module('dgc.details').controller('DetailsController', ['$window', '$scope', '$state', '$stateParams', 'DetailsResource', 'SchemaResource',
function($window, $scope, $state, $stateParams, DetailsResource) { function($window, $scope, $state, $stateParams, DetailsResource, SchemaResource) {
$scope.tableName = false; $scope.tableName = false;
$scope.isTable = false; $scope.isTable = false;
DetailsResource.get({ DetailsResource.get({
id: $stateParams.id id: $stateParams.id
}, function(data) { }, function(data) {
$scope.details = data; $scope.details = data;
$scope.schemas = data;
$scope.tableName = data.values.name; $scope.tableName = data.values.name;
$scope.isTable = (typeof data.typeName !== 'undefined' && data.typeName.toLowerCase().indexOf('table') !== -1) ? true : false; $scope.isTable = (typeof data.typeName !== 'undefined' && data.typeName.toLowerCase().indexOf('table') !== -1) ? true : false;
$scope.onActivate('io'); $scope.onActivate('io');
$scope.isTags = (typeof data.traits !== 'undefined' && typeof data.traits === 'object') ? true : false; $scope.isTags = (typeof data.traits !== 'undefined' && typeof data.traits === 'object') ? true : false;
if (data && data.values && data.values.name && data.values.name !== "") {
SchemaResource.get({
tableName: data.values.name
}, function(data1) {
if (data1.results) {
$scope.schema = data1.results.rows;
$scope.isSchema = (data1.results.rows && data1.results.rows.length > 0) ? true : false;
for (var t = 0; t < data1.results.rows.length; t++) {
if (data1.results.rows[t].$id$) {
$scope.isTraitId = true;
}
if (data1.results.rows[t].type) {
$scope.isHiveSchema = true;
}
if($scope.isTraitId && $scope.isHiveSchema){
break;
}
}
}
});
}
}); });
$scope.isNumber = angular.isNumber; $scope.isNumber = angular.isNumber;
$scope.isObject = angular.isObject; $scope.isObject = angular.isObject;
$scope.isString = angular.isString; $scope.isString = angular.isString;
...@@ -45,6 +68,13 @@ angular.module('dgc.details').controller('DetailsController', ['$window', '$scop ...@@ -45,6 +68,13 @@ angular.module('dgc.details').controller('DetailsController', ['$window', '$scop
}); });
}; };
$scope.openAddTagHome = function(traitId) {
$state.go('addTagDetails', {
tId: traitId
});
};
$scope.goDetails = function(id) { $scope.goDetails = function(id) {
$state.go("details", { $state.go("details", {
id: id id: id
......
...@@ -37,6 +37,18 @@ angular.module('dgc.details').factory('DetailsResource', ['$resource', function( ...@@ -37,6 +37,18 @@ angular.module('dgc.details').factory('DetailsResource', ['$resource', function(
method: 'DELETE', method: 'DELETE',
url: '/api/atlas/entities/:id/traits/:tagName' url: '/api/atlas/entities/:id/traits/:tagName'
} }
}); });
}]).factory('SchemaResource', ['$resource', function($resource) {
return $resource('/api/atlas/lineage/hive/table/:tableName/schema', {}, {
get: {
method: 'GET',
transformResponse: function(data) {
if (data) {
return angular.fromJson(data);
}
},
responseType: 'json'
}
});
}]); }]);
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
'use strict'; 'use strict';
angular.module('dgc.details').config(['$stateProvider', angular.module('dgc.details').config(['$stateProvider',
...@@ -26,5 +25,27 @@ angular.module('dgc.details').config(['$stateProvider', ...@@ -26,5 +25,27 @@ angular.module('dgc.details').config(['$stateProvider',
url: '/details/:id', url: '/details/:id',
templateUrl: '/modules/details/views/details.html' templateUrl: '/modules/details/views/details.html'
}); });
$stateProvider.state('addTagDetails', {
parent: 'details',
params: {
tId: null,
frm : 'details'
},
onEnter: ['$stateParams', '$state', '$modal', 'NavigationResource', function($stateParams, $state, $modal, NavigationResource) {
$modal.open({
templateUrl: '/modules/tags/instance/views/createTag.html',
controller: 'CreateTagController',
windowClass: 'create-tag-entity',
resolve: {
typesList: function() {
return NavigationResource.get().$promise;
}
}
}).result.finally(function() {
$state.go('^');
});
}]
});
} }
]); ]);
\ No newline at end of file
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF 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.
-->
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF 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.
-->
<div class="row detailsPage" data-ng-controller="DetailsController"> <div class="row detailsPage" data-ng-controller="DetailsController">
<div class="col-lg-12 padding0"> <div class="col-lg-12 padding0">
<ul class="breadcrumb"> <ul class="breadcrumb">
<li><button class="btn btn-link" data-ng-click="goBack()"><i class="fa fa-arrow-left"></i> Back To Result</button> </li> <li><button class="btn btn-link" data-ng-click="goBack()"><i class="fa fa-arrow-left"></i> Back To Result</button> </li>
</ul> </ul>
</div> </div>
<div role="tabpanel" class="col-lg-12 padding0"> <div role="tabpanel" class="col-lg-12 padding0">
<div class="mB20"> <div class="mB20">
<h4><b>Name:</b> <span class="black">{{details.values.name}}</span></h2> <h4 ng-if="details.values && details.values.name && details.values.name != ''">
<h4><b>Description:</b> <span class="black">{{details.values.description}}</span></h4> <b>Name:</b> <span class="black">{{details.values.name}}</span></h2>
<h4 data-ng-show="isTable" data-disable="!tableName" data-select="onActivate('io')"><span class="lineage">Lineage</span> <ng-include data-table-type="io" src="'/modules/lineage/views/lineage_io.html'"/></h4> <h4 ng-if="details.values && details.values.description && details.values.description != ''"><b>Description:</b> <span class="black">{{details.values.description}}</span></h4>
<h4 data-ng-show="isTable" data-disable="!tableName" data-select="onActivate('io')">
<span class="lineage">Lineage</span>
<ng-include data-table-type="io" src="'/modules/lineage/views/lineage_io.html'"/>
</h4>
</div> </div>
<tabset> <tabset>
<tab heading="Details"> <tab heading="Details">
<table class="table table-bordered"> <table class="table table-bordered">
<thead> <thead>
<tr> <tr>
<th>Key</th> <th>Key</th>
<th>Value</th> <th>Value</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr data-ng-repeat="(key,value) in details.values" ng-if="value && !(key==='columns') && !(key==='name') && !(key==='description')"> <tr data-ng-repeat="(key,value) in details.values" ng-if="value && !(key==='columns') && !(key==='name') && !(key==='description')">
<td>{{key}}</td>
<td>{{key}}</td> <td data-ng-if="isObject(value) && isString(value.id)" class="pointer">
<a data-ui-sref="details({id:value.id})">{{ value.id }}</a>
<td data-ng-if="isObject(value) && isString(value.id)" class="pointer"> </td>
<a data-ui-sref="details({id:value.id})">{{ value.id }}</a> <td data-ng-if="isObject(value) && isObject(value.id) && isString(value.id.id)" class="pointer"> <a data-ui-sref="details({id:value.id.id})">{{ value.id.id }}</a>
</td> </td>
<td data-ng-if="isArray(value)">
<td data-ng-if="isObject(value) && isObject(value.id) && isString(value.id.id)" class="pointer"><a data-ui-sref="details({id:value.id.id})">{{ value.id.id }}</a></td> <div class="row" data-ng-repeat="(key1, value1) in value" ng-if="value1">
<div data-ng-if="isObject(value1)" data-ng-repeat="(key2, value2) in value1" >
<td data-ng-if="isArray(value)"> <a data-ng-if="isString(value2) && key2 == 'id'" class="pointer pdLft15px" data-ui-sref="details({id:value2})">{{ value2 }}</a>
<div class="row" data-ng-repeat="(key1, value1) in value" ng-if="value1"> </div>
<div data-ng-if="isObject(value1)" data-ng-repeat="(key2, value2) in value1" > </div>
<a data-ng-if="isString(value2) && key2 == 'id'" class="pointer pdLft15px" data-ui-sref="details({id:value2})">{{ value2 }}</a> </td>
</div> <td data-ng-if="!isArray(value) && isObject(value[0]) && isString(value[0].id) && key=='inputTables'" data-ng-click="goDetails(value[0].id)" class="pointer">
</div> <div class="row" data-ng-repeat="(key1, value1) in value[0]" ng-if="value1">
</td> <div class="col-md-6" data-ng-if="!isString(value1)" data-ng-repeat="(key2, value2) in value1 track by $index"></div>
<div data-ng-if="isString(value2)" data-ng-repeat="(key3, value3) in value2"> {{key3}}: {{value3}}</div>
<td data-ng-if="!isArray(value) && isObject(value[0]) && isString(value[0].id) && key=='inputTables'" data-ng-click="goDetails(value[0].id)" class="pointer"> <div class="col-md-6" data-ng-if="isString(value1)"> {{key1}} : {{value1 | date:'medium'}} UTC</div>
</div>
<div class="row" data-ng-repeat="(key1, value1) in value[0]" ng-if="value1"> </td>
<div class="col-md-6" data-ng-if="!isString(value1)" data-ng-repeat="(key2, value2) in value1 track by $index"></div> <td data-ng-if="isNumber(value)">{{value * 1000 | date:'yyyy-MM-dd HH:mm:ss'}} UTC</td>
<div data-ng-if="isString(value2)" data-ng-repeat="(key3, value3) in value2"> {{key3}}: {{value3}}</div> <td data-ng-if="isString(value)">{{value}}</td>
<div class="col-md-6" data-ng-if="isString(value1)"> {{key1}} : {{value1 | date:'medium'}} UTC</div> </tr>
</div>
</td>
<td data-ng-if="isNumber(value)">{{value * 1000 | date:'yyyy-MM-dd HH:mm:ss'}} UTC</td>
<td data-ng-if="isString(value)">{{value}}</td>
</tr>
</tbody> </tbody>
</table> </table>
</tab> </tab>
<tab data-heading="Schema" data-ng-if="isTable"> <tab data-heading="Schema" data-ng-if="isSchema">
<ng-include src="'/modules/details/views/schema.html'"/> <ng-include src="'/modules/details/views/schema.html'"/>
</tab> </tab>
<tab data-heading="Tags" data-ng-if="isTags"> <tab data-heading="Tags" data-ng-if="isTags">
<ng-include src="'/modules/tags/instance/views/tags.html'"/> <ng-include src="'/modules/tags/instance/views/tags.html'"/>
</tab> </tab>
</tabset> </tabset>
</div> </div>
</div> </div>
\ No newline at end of file
<!-- <!--
~ Licensed to the Apache Software Foundation (ASF) under one ~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file ~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information ~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file ~ regarding copyright ownership. The ASF licenses this file
~ to you under the Apache License, Version 2.0 (the ~ to you under the Apache License, Version 2.0 (the
~ "License"); you may not use this file except in compliance ~ "License"); you may not use this file except in compliance
~ with the License. You may obtain a copy of the License at ~ with the License. You may obtain a copy of the License at
~ ~
~ http://www.apache.org/licenses/LICENSE-2.0 ~ http://www.apache.org/licenses/LICENSE-2.0
~ ~
~ Unless required by applicable law or agreed to in writing, software ~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS, ~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ limitations under the License.
--> -->
<table class="table table-bordered"> <table class="table table-bordered">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Comment</th> <th>Comment</th>
<th>DataType</th> <th ng-if="!isHiveSchema">DataType</th>
</tr> <th ng-if="isHiveSchema">Type</th>
<th ng-if="isTraitId">Tags </th>
<th ng-if="isTraitId">Tools</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="colm in details.values.columns"> <tr ng-repeat="colm in schema">
<td> {{colm.values.name}}</td> <td>
<td>{{colm.values.comment}}</td> <a ng-if="colm['$id$']" data-ui-sref="details({id:colm['$id$'].id})">{{colm.name}}</a>
<td> {{colm.values.dataType}}</td> <span ng-if="!colm['$id$']"> {{colm.name}} </span>
</td>
</tr> <td>{{colm.comment}}</td>
<td ng-if="!isHiveSchema"> {{colm.dataType}}</td>
<td ng-if="isHiveSchema"> {{colm.type}}</td>
<td ng-if="colm['$id$']">
<div id="{{colm['$id$'].id}}_schema" class="wordBreak"><a class="tabsearchanchor" ng-repeat="(key, value) in colm['$traits$']" data-ui-sref="search({query: key})" title="{{key}}">{{key}}<span> </span></a></div>
</td>
<td ng-if="colm['$id$']" class="addTag">
<img ng-src="img/addTag.png" tooltip="Add Tag" ng-click="openAddTagHome(colm['$id$'].id )">
</td>
</tr>
</tbody> </tbody>
</table> </table>
\ No newline at end of file
...@@ -191,7 +191,7 @@ angular.module('dgc.search').controller('SearchController', ['$scope', '$locatio ...@@ -191,7 +191,7 @@ angular.module('dgc.search').controller('SearchController', ['$scope', '$locatio
}; };
$scope.openAddTagHome = function(traitId) { $scope.openAddTagHome = function(traitId) {
$state.go('addTagHome', { $state.go('addTagHome', {
id: traitId tId: traitId
}); });
}; };
$scope.isTag = function(typename) { $scope.isTag = function(typename) {
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
'use strict'; 'use strict';
//Setting up route //Setting up route
...@@ -28,7 +27,10 @@ angular.module('dgc.search').config(['$stateProvider', ...@@ -28,7 +27,10 @@ angular.module('dgc.search').config(['$stateProvider',
}); });
$stateProvider.state('addTagHome', { $stateProvider.state('addTagHome', {
parent: 'search', parent: 'search',
params: { id:null}, params: {
tId: null,
frm : 'search'
},
onEnter: ['$stateParams', '$state', '$modal', 'NavigationResource', function($stateParams, $state, $modal, NavigationResource) { onEnter: ['$stateParams', '$state', '$modal', 'NavigationResource', function($stateParams, $state, $modal, NavigationResource) {
$modal.open({ $modal.open({
templateUrl: '/modules/tags/instance/views/createTag.html', templateUrl: '/modules/tags/instance/views/createTag.html',
...@@ -45,4 +47,4 @@ angular.module('dgc.search').config(['$stateProvider', ...@@ -45,4 +47,4 @@ angular.module('dgc.search').config(['$stateProvider',
}] }]
}); });
} }
]); ]);
\ No newline at end of file
...@@ -81,7 +81,9 @@ ...@@ -81,7 +81,9 @@
<span data-ng-if="res.toLowerCase().indexOf('time') != -1 && isNumber(result[res])">{{result[res] * 1000 | date:'yyyy-MM-dd HH:mm:ss'}} UTC</span> <span data-ng-if="res.toLowerCase().indexOf('time') != -1 && isNumber(result[res])">{{result[res] * 1000 | date:'yyyy-MM-dd HH:mm:ss'}} UTC</span>
<a data-ng-if="res.toLowerCase().indexOf('name') != -1" data-ui-sref="details({id:result['id']|| result['guid']})">{{result[res]}}</a> <a data-ng-if="res.toLowerCase().indexOf('name') != -1 && result['id']" data-ui-sref="details({id:result['id']|| result['guid']})">{{result[res]}}</a>
<span data-ng-if="res.toLowerCase().indexOf('name') != -1 && !result['id']">{{result[res]}}</span>
<div data-ng-if="res == '$traits$'" class="wordBreak tags" id="{{result['id']|| result['guid']}}"> <div data-ng-if="res == '$traits$'" class="wordBreak tags" id="{{result['id']|| result['guid']}}">
<a class="tabsearchanchor" ng-repeat="(key, value) in result[res]" data-ui-sref="search({query: key})" title="{{key}}">{{key}}<span> </span></a> <a class="tabsearchanchor" ng-repeat="(key, value) in result[res]" data-ui-sref="search({query: key})" title="{{key}}">{{key}}<span> </span></a>
......
...@@ -42,8 +42,7 @@ angular.module('dgc.tags.instance').controller('CreateTagController', ['$scope', ...@@ -42,8 +42,7 @@ angular.module('dgc.tags.instance').controller('CreateTagController', ['$scope',
for (var t = 0; t < traitTypes.length; t++) { for (var t = 0; t < traitTypes.length; t++) {
if (traitTypes[t]) { if (traitTypes[t]) {
for(var indx = 0; indx < traitTypes[t].attributeDefinitions.length; indx++) for (var indx = 0; indx < traitTypes[t].attributeDefinitions.length; indx++) {
{
var attrDefn = traitTypes[t].attributeDefinitions[indx]; var attrDefn = traitTypes[t].attributeDefinitions[indx];
$scope.propertiesList[attrDefn.name] = ''; $scope.propertiesList[attrDefn.name] = '';
$scope.isRequired[attrDefn.name] = attrDefn.isRequired; $scope.isRequired[attrDefn.name] = attrDefn.isRequired;
...@@ -67,14 +66,18 @@ angular.module('dgc.tags.instance').controller('CreateTagController', ['$scope', ...@@ -67,14 +66,18 @@ angular.module('dgc.tags.instance').controller('CreateTagController', ['$scope',
"values": $scope.propertiesList "values": $scope.propertiesList
}; };
DetailsResource.saveTag({ DetailsResource.saveTag({
id: $stateParams.id id: $stateParams.tId
}, requestObject).$promise.then(function(data) { }, requestObject).$promise.then(function(data) {
if (data.requestId !== undefined && data.GUID === $stateParams.id) { if (data.requestId !== undefined && data.GUID === $stateParams.tId) {
var tagName = $$("#tagDefinition").val(); var tagName = $$("#tagDefinition").val();
$rootScope.updateTags(true, { if($stateParams.frm && $stateParams.frm !== 'details'){
added: $scope.selectedType $rootScope.updateTags(true, {
}); added: $scope.selectedType
$$("#" + $stateParams.id).append("<a class='tabsearchanchor ng-binding ng-scope' data-ui-sref='search({query: " + tagName + "})' title='" + tagName + "' href='#!/search?query=" + tagName + "'>" + tagName + "<span> </span></a>"); });
$$("#" + $stateParams.tId).append("<a class='tabsearchanchor ng-binding ng-scope' data-ui-sref='search({query: " + tagName + "})' title='" + tagName + "' href='#!/search?query=" + tagName + "'>" + tagName + "<span> </span></a>");
}else if($stateParams.frm === 'details'){
$$("#" + $stateParams.tId+"_schema").append("<a class='tabsearchanchor ng-binding ng-scope' data-ui-sref='search({query: " + tagName + "})' title='" + tagName + "' href='#!/search?query=" + tagName + "'>" + tagName + "<span> </span></a>");
}
} }
NotificationService.info('Tag "' + $scope.selectedType + '" has been added to entity', true); NotificationService.info('Tag "' + $scope.selectedType + '" has been added to entity', true);
$modalInstance.close(true); $modalInstance.close(true);
......
...@@ -61,7 +61,7 @@ angular.module('dgc.tags.instance').controller('InstanceTagController', ['$scope ...@@ -61,7 +61,7 @@ angular.module('dgc.tags.instance').controller('InstanceTagController', ['$scope
$scope.openAddTag = function() { $scope.openAddTag = function() {
$state.go('addTag', { $state.go('addTag', {
id: $scope.id tId: $scope.id
}); });
}; };
......
...@@ -26,6 +26,10 @@ angular.module('dgc.tags').config(['$stateProvider', ...@@ -26,6 +26,10 @@ angular.module('dgc.tags').config(['$stateProvider',
}); });
$stateProvider.state('addTag', { $stateProvider.state('addTag', {
parent: 'details', parent: 'details',
params: {
tId: null,
frm : 'addTag'
},
onEnter: ['$stateParams', '$state', '$modal', 'NavigationResource', function($stateParams, $state, $modal, NavigationResource) { onEnter: ['$stateParams', '$state', '$modal', 'NavigationResource', function($stateParams, $state, $modal, NavigationResource) {
$modal.open({ $modal.open({
templateUrl: '/modules/tags/instance/views/createTag.html', templateUrl: '/modules/tags/instance/views/createTag.html',
......
...@@ -14,6 +14,7 @@ ATLAS-54 Rename configs in hive hook (shwethags) ...@@ -14,6 +14,7 @@ ATLAS-54 Rename configs in hive hook (shwethags)
ATLAS-3 Mixed Index creation fails with Date types (sumasai via shwethags) ATLAS-3 Mixed Index creation fails with Date types (sumasai via shwethags)
ALL CHANGES: ALL CHANGES:
ATLAS-376 UI: Use the Schema API of the backend to populate details for Schema tab (darshankumar89 via sumasai)
ATLAS-380 Fix ATLAS source artifact generation (sumasai) ATLAS-380 Fix ATLAS source artifact generation (sumasai)
ATLAS-354 Kerberized cluster: quick_start.py fails to add sample data (shwethags) ATLAS-354 Kerberized cluster: quick_start.py fails to add sample data (shwethags)
ATLAS-47 Entity mutations for complex types (sumasai via shwethags) ATLAS-47 Entity mutations for complex types (sumasai via shwethags)
......
Markdown is supported
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