Added tags to platform create page and basic backend integration api implementation.

feature/appm-store/pbac
Menaka Jayawardena 7 years ago
parent 611eb78554
commit b3d2abf99e

@ -11,38 +11,40 @@
"dependencies": { "dependencies": {
"axios": "^0.16.2", "axios": "^0.16.2",
"flux": "^3.1.3", "flux": "^3.1.3",
"history": "^4.6.3", "history": "^4.7.2",
"latest-version": "^3.1.0", "latest-version": "^3.1.0",
"material-ui": "^0.19.0", "material-ui": "^0.19.1",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"qs": "^6.5.0", "qs": "^6.5.0",
"react": "^15.6.1", "react": "^15.6.1",
"react-dom": "^15.6.1", "react-dom": "^15.6.1",
"react-dropzone": "^4.1.0", "react-dropzone": "^4.1.2",
"react-images-uploader": "^1.1.0", "react-images-uploader": "^1.1.0",
"react-material-ui-form-validator": "^0.5.0", "react-material-ui-form-validator": "^0.5.1",
"react-modal": "^2.2.2", "react-modal": "^2.3.2",
"react-router": "^4.1.2", "react-router": "^4.2.0",
"react-router-dom": "^4.1.2", "react-router-dom": "^4.2.2",
"react-scripts": "1.0.10", "react-scripts": "1.0.10",
"react-sliding-pane": "^1.2.3", "react-sliding-pane": "^1.2.3",
"react-tap-event-plugin": "^2.0.1" "react-tap-event-plugin": "^2.0.1"
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.24.1", "babel-core": "^6.26.0",
"babel-loader": "^7.0.0", "babel-loader": "^7.1.2",
"babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"chai": "^4.0.2",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"babel-register": "^6.24.1", "babel-register": "^6.26.0",
"css-loader": "^0.28.2", "chai": "^4.1.2",
"css-loader": "^0.28.7",
"less": "^2.7.2", "less": "^2.7.2",
"less-loader": "^4.0.4", "less-loader": "^4.0.4",
"mocha": "^3.4.1", "mocha": "^3.4.1",
"mock-local-storage": "^1.0.2", "mock-local-storage": "^1.0.2",
"node-sass": "^4.5.3",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.1", "style-loader": "^0.18.1",
"webpack": "^2.5.0" "webpack": "^2.7.0"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

@ -16,7 +16,7 @@
* under the License. * under the License.
*/ */
import './App.css'; import './App.scss';
import React, {Component} from 'react'; import React, {Component} from 'react';
import createHistory from 'history/createBrowserHistory'; import createHistory from 'history/createBrowserHistory';
import {BrowserRouter as Router, Redirect, Route, Switch} from 'react-router-dom' import {BrowserRouter as Router, Redirect, Route, Switch} from 'react-router-dom'
@ -54,12 +54,21 @@ class Base extends Component {
constructor() { constructor() {
super(); super();
this.state = { this.state = {
user: "admin" user: "s"
} }
} }
componentWillMount() {
}
componentDidMount() {
}
render() { render() {
if (this.state.user) { if (this.state.user) {
console.log("Have User.");
return ( return (
<div className="container"> <div className="container">
<BaseLayout> <BaseLayout>
@ -80,9 +89,12 @@ class Base extends Component {
</BaseLayout> </BaseLayout>
</div> </div>
) )
} } else {
console.log("No user");
return (<Redirect to={"/login"}/>) return (<Redirect to={"/login"}/>)
} }
}
} }
/** /**

@ -15,3 +15,31 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
'use strict';
import Axios from 'axios';
/**
* Handles all tasks related to Authentication and Authorization.
* Generate access tokens, verify the user has necessary permissions etc.
* */
class AuthHandler {
/**
* Generate client id and client secret to generate access tokens.
* */
login(userName, password) {
Axios.post("https://localhost:9443/auth/application-mgt/v1.0/auth/tokens?userName=admin&password=admin").then()
}
isLoggedIn() {
}
getloggedInUser() {
}
}

@ -15,7 +15,106 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
'use strict';
import Helper from './helpers/AppMgtApiHelpers';
/**
* Application related apis
* */
export default class Endpoint{
/**
* Api for create an application.
* @param: applicationData: The application data object. This contains an object array of each step data from
* application creation wizard.
*
* From that data array, the proper application object is created and send it to the api.
* */
static createApplication(applicationData) {
console.log("In application create application", applicationData);
Helper.buildApplication(applicationData);
}
/**
* Method to handle application release process.
* */
static releaseApplication() {
}
/**
* Edit created application.
* @param applicationData: The modified application data.
* */
static editApplication(applicationData) {
}
/** /**
* Defines the list of App Manager APIs. * Get all the created applications for the user.
* */ * */
static getApplications() {
}
/**
* Get specific application.
* @param appId : The application Id.
* */
static getApplication(appId) {
}
/**
* Delete specified application.
* @param appId: The id of the application which is to be deleted.
* */
static deleteApplication(appId) {
}
/**
* Platform related apis
* */
/**
* Create a new Platform
* @param platformData: The platform data object.
* */
static createPlatform(platformData) {
// /api/application-mgt/v1.0/platforms/1.0.0/
// {
// identifier: "${platform_identifier}",
// name: "New Platform",
// description : "New Platform"
// }
}
/**
* Get available platforms
* */
static getPlatforms() {
}
/**
* Get the user specified platform
* @param platformId: The identifier of the platform
* */
static getPlatform(platformId) {
}
/**
* Delete specified platform
* @param platformId: The id of the platform which is to be deleted.
* */
static deletePlatform(platformId) {
}
}

@ -0,0 +1,49 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.
*/
'use strict';
/**
* Helper methods for app publisher.
* */
export default class Helper {
/**
* Generate application object from form data passed.
* @param appData: Application data from the application creation form.
* */
static buildApplication(appData) {
let application = {};
let images = {};
for (var step in appData) {
let tmpData = appData[step].data.step;
for (var prop in tmpData) {
if (prop === 'banner' || prop === 'screenshots' || prop === 'icon') {
images[prop] = tmpData[prop];
} else {
application[prop] = tmpData[prop];
}
}
}
console.log(application, images);
}
}

@ -15,3 +15,17 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
scopes = 'perm:application:get perm:application:create perm:application:update perm:application-mgt:login' +
' perm:application:delete perm:platform:add perm:platform:remove perm:roles:view perm:devices:view';
TOKEN_ENDPOINT = '/token';
DYNAMIC_CLIENT_REGISTER_ENDPOINT = '/api-application-registration/register';
appManagerEndpoints = {
GET_ALL_APPS: '/api/application-mgt/v1.0/applications/1.0.0/',
CREATE_APP: '/api/application-mgt/v1.0/applications/1.0.0/',
UPLOAD_IMAGES: '/api/application-mgt/v1.0/applications/1.0.0/upload-image-artifacts/', //+appId
};

@ -18,6 +18,7 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import Dialog from 'material-ui/Dialog'; import Dialog from 'material-ui/Dialog';
import Endpoint from '../../api/Endpoints';
import {withRouter} from 'react-router-dom'; import {withRouter} from 'react-router-dom';
import FlatButton from 'material-ui/FlatButton'; import FlatButton from 'material-ui/FlatButton';
import {Step1, Step2, Step3} from './CreateSteps'; import {Step1, Step2, Step3} from './CreateSteps';
@ -80,6 +81,8 @@ class ApplicationCreate extends Component {
* */ * */
handleSubmit = () => { handleSubmit = () => {
console.log(this.state.stepData); console.log(this.state.stepData);
Endpoint.createApplication(this.state.stepData);
}; };
/** /**
@ -164,11 +167,10 @@ class ApplicationCreate extends Component {
setData={this.setStepData} setData={this.setStepData}
removeData={this.removeStepData}/>; removeData={this.removeStepData}/>;
default: default:
return 'You\'re a long way from home sonny jim!'; return <div></div>;
} }
} }
render() { render() {
const {finished, stepIndex} = this.state; const {finished, stepIndex} = this.state;
const contentStyle = {margin: '0 16px'}; const contentStyle = {margin: '0 16px'};
@ -189,7 +191,6 @@ class ApplicationCreate extends Component {
/>, />,
]; ];
return ( return (
<div className="middle createapplicationmiddle"> <div className="middle createapplicationmiddle">
<Card className="creataapplicationcard"> <Card className="creataapplicationcard">

@ -56,16 +56,16 @@ class Step2 extends Component {
super(); super();
this.state = { this.state = {
tags: [], tags: [],
icon: [],
title: "",
errors: {},
banner: [],
defValue: "", defValue: "",
category: 0, category: 0,
visibility: 0, visibility: 0,
errors: {},
title: "",
shortDescription: "",
description: "", description: "",
banner: [],
screenshots: [], screenshots: [],
icon: [] shortDescription: ""
}; };
this.scriptId = "application-create-step2"; this.scriptId = "application-create-step2";
} }
@ -243,13 +243,14 @@ class Step2 extends Component {
* */ * */
_setStepData() { _setStepData() {
let stepData = { let stepData = {
title: this.state.title,
description: this.state.description,
shortDescription: this.state.shortDescription,
tags: this.state.tags, tags: this.state.tags,
icon: this.state.icon,
title: this.state.title,
banner: this.state.banner, banner: this.state.banner,
category: this.state.category,
screenshots: this.state.screenshots, screenshots: this.state.screenshots,
icon: this.state.icon description: this.state.description,
shortDescription: this.state.shortDescription
}; };
this.props.setData("step2", {step: stepData}); this.props.setData("step2", {step: stepData});
@ -346,7 +347,7 @@ class Step2 extends Component {
<TextField <TextField
id="tags" id="tags"
errorText={this.state.errors["tags"]} errorText={this.state.errors["tags"]}
hintText="Enter application tags.." hintText="Enter Application tags.."
floatingLabelText="Tags*" floatingLabelText="Tags*"
floatingLabelFixed={true} floatingLabelFixed={true}
value={this.state.defValue} value={this.state.defValue}

@ -17,6 +17,8 @@
*/ */
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Axios from 'axios';
import Chip from 'material-ui/Chip';
import Dropzone from 'react-dropzone'; import Dropzone from 'react-dropzone';
import React, {Component} from 'react'; import React, {Component} from 'react';
import Toggle from 'material-ui/Toggle'; import Toggle from 'material-ui/Toggle';
@ -48,6 +50,8 @@ class PlatformCreate extends Component {
constructor() { constructor() {
super(); super();
this.state = { this.state = {
tags: [],
defValue: "",
enabled: true, enabled: true,
allTenants: false, allTenants: false,
files: [], files: [],
@ -104,6 +108,55 @@ class PlatformCreate extends Component {
this.setState({selectedProperty: value}); this.setState({selectedProperty: value});
}; };
/**
* Handles Chip delete function.
* Removes the tag from state.tags
* */
_handleTagDelete = (key) => {
this.chipData = this.state.tags;
const chipToDelete = this.chipData.map((chip) => chip.key).indexOf(key);
this.chipData.splice(chipToDelete, 1);
this.setState({tags: this.chipData});
};
/**
* Create a tag on Enter key press and set it to the state.
* Clears the tags text field.
* Chip gets two parameters: Key and value.
* */
_addTags(event) {
let tags = this.state.tags;
if (event.charCode === 13) {
event.preventDefault();
tags.push({key: Math.floor(Math.random() * 1000), value: event.target.value});
this.setState({tags, defValue: ""});
}
}
/**
* Creates Chip array from state.tags.
* */
_renderChip(data) {
return (
<Chip
key={data.key}
onRequestDelete={() => this._handleTagDelete(data.key)}
style={this.styles.chip}
>
{data.value}
</Chip>
);
}
/**
* Set the value for tag.
* */
_handleTagChange(event) {
let defaultValue = this.state.defValue;
defaultValue = event.target.value;
this.setState({defValue: defaultValue})
}
/** /**
* Remove the selected property from the property list. * Remove the selected property from the property list.
* */ * */
@ -161,6 +214,10 @@ class PlatformCreate extends Component {
}; };
_onCreatePlatform() { _onCreatePlatform() {
//Call the platform create api.
let platform = {};
} }
@ -193,6 +250,8 @@ class PlatformCreate extends Component {
selectedProperty, selectedProperty,
propertyTypes, propertyTypes,
name, name,
tags,
defValue,
description, description,
property} = this.state; property} = this.state;
@ -236,6 +295,19 @@ class PlatformCreate extends Component {
onToggle={this._handleToggle.bind(this)} onToggle={this._handleToggle.bind(this)}
toggled={enabled} toggled={enabled}
/> <br/> /> <br/>
<TextField
id="tags"
hintText="Enter Platform tags.."
floatingLabelText="Tags*"
floatingLabelFixed={true}
value={defValue}
onChange={this._handleTagChange.bind(this)}
onKeyPress={this._addTags.bind(this)}
/><br/>
<div style={this.styles.wrapper}>
{tags.map(this._renderChip, this)}
</div>
<br/>
<div> <div>
<p className="createplatformproperties">Platform Properties</p> <p className="createplatformproperties">Platform Properties</p>
<div id="property-container"> <div id="property-container">

@ -97,8 +97,8 @@ class PlatformListing extends Component {
handleRowClick={this._onRowClick.bind(this)} handleRowClick={this._onRowClick.bind(this)}
noDataMessage={{type: 'button', text: 'Create Platform'}}/> noDataMessage={{type: 'button', text: 'Create Platform'}}/>
</Card> </Card>
</div>
</div>); );
} }
} }

@ -20,10 +20,10 @@ import qs from 'qs';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, {Component} from 'react'; import React, {Component} from 'react';
import Checkbox from 'material-ui/Checkbox'; import Checkbox from 'material-ui/Checkbox';
import TextField from 'material-ui/TextField';
import {Redirect, Switch} from 'react-router-dom'; import {Redirect, Switch} from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
import {Card, CardActions, CardTitle} from 'material-ui/Card'; import {Card, CardActions, CardTitle} from 'material-ui/Card';
import {TextValidator, ValidatorForm} from 'react-material-ui-form-validator';
//todo: remove the {TextValidator, ValidatorForm} and implement it manually. //todo: remove the {TextValidator, ValidatorForm} and implement it manually.
@ -40,36 +40,43 @@ class Login extends Component {
constructor() { constructor() {
super(); super();
this.state = { this.state = {
isLoggedIn: true, isLoggedIn: false,
referrer: "/", referrer: "/",
userName: "", userName: "",
password: "", password: "",
rememberMe: true rememberMe: true,
errors: {}
} }
} }
componentDidMount() { componentWillMount() {
let queryString = this.props.location.search; console.log("IN Login")
console.log(queryString);
queryString = queryString.replace(/^\?/, '');
/* With QS version up we can directly use {ignoreQueryPrefix: true} option */
let params = qs.parse(queryString);
if (params.referrer) {
this.setState({referrer: params.referrer});
} }
componentDidMount() {
console.log("in Login")
// let queryString = this.props.location.search;
// console.log(queryString);
// queryString = queryString.replace(/^\?/, '');
// /* With QS version up we can directly use {ignoreQueryPrefix: true} option */
// let params = qs.parse(queryString);
// if (params.referrer) {
// this.setState({referrer: params.referrer});
// }
} }
handleLogin(event) { _handleLogin(event) {
event.preventDefault(); event.preventDefault();
this._validateForm();
} }
/** /**
* Handles the username field change event. * Handles the username field change event.
* */ * */
onUserNameChange(event) { _onUserNameChange(event, value) {
this.setState( this.setState(
{ {
userName: event.target.value userName: value
} }
); );
} }
@ -77,10 +84,10 @@ class Login extends Component {
/** /**
* Handles the password field change event. * Handles the password field change event.
* */ * */
onPasswordChange(event) { _onPasswordChange(event, value) {
this.setState( this.setState(
{ {
password: event.target.value password: value
} }
); );
} }
@ -88,7 +95,7 @@ class Login extends Component {
/** /**
* Handles the remember me check. * Handles the remember me check.
* */ * */
handleRememberMe() { _handleRememberMe() {
this.setState( this.setState(
{ {
rememberMe: !this.state.rememberMe rememberMe: !this.state.rememberMe
@ -96,6 +103,22 @@ class Login extends Component {
); );
} }
/**
* Validate the login form.
* */
_validateForm() {
let errors = {};
if (!this.state.password) {
errors["passwordError"] = "Password is Required";
}
if (!this.state.userName) {
errors["userNameError"] = "User Name is Required";
}
this.setState({errors: errors}, console.log(errors));
}
render() { render() {
if (!this.state.isLoggedIn) { if (!this.state.isLoggedIn) {
@ -107,37 +130,32 @@ class Login extends Component {
<Card> <Card>
<CardTitle title="WSO2 IoT App Publisher"/> <CardTitle title="WSO2 IoT App Publisher"/>
<CardActions> <CardActions>
<ValidatorForm <form onSubmit={this._handleLogin.bind(this)}>
ref="form" <TextField
onSubmit={this.handleLogin.bind(this)} hintText="Enter the User Name."
onError={errors => console.log(errors)}> id="username"
<TextValidator errorText={this.state.errors["userNameError"]}
floatingLabelText="User Name" floatingLabelText="User Name*"
floatingLabelFixed={true} floatingLabelFixed={true}
onChange={this.onUserNameChange.bind(this)}
name="userName"
validators={['required']}
errorMessages={['User Name is required']}
value={this.state.userName} value={this.state.userName}
/> onChange={this._onUserNameChange.bind(this)}
<br/> /><br/>
<TextValidator <TextField
floatingLabelText="Password" hintText="Enter the Password."
floatingLabelFixed={true} id="password"
onChange={this.onPasswordChange.bind(this)}
name="password"
type="password" type="password"
errorText={this.state.errors["passwordError"]}
floatingLabelText="Password*"
floatingLabelFixed={true}
value={this.state.password} value={this.state.password}
validators={['required']} onChange={this._onPasswordChange.bind(this)}
errorMessages={['Password is required']} /><br/>
/>
<br/>
<Checkbox label="Remember me." <Checkbox label="Remember me."
onCheck={this.handleRememberMe.bind(this)} onCheck={this._handleRememberMe.bind(this)}
checked={this.state.rememberMe}/> checked={this.state.rememberMe}/>
<br/> <br/>
<RaisedButton type="submit" label="Login"/> <RaisedButton type="submit" label="Login"/>
</ValidatorForm> </form>
</CardActions> </CardActions>
</Card> </Card>
</div>); </div>);

Loading…
Cancel
Save