Add input validation for contributions (#153)

This commit is contained in:
Neha Gupta 2019-04-26 15:49:25 -07:00 коммит произвёл GitHub
Родитель a60a7676a4
Коммит 73f13eeb3b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 146 добавлений и 34 удалений

Просмотреть файл

@ -6,17 +6,19 @@ import {
TextField,
PrimaryButton,
DefaultButton,
Dropdown
Dropdown,
Dialog
} from "office-ui-fabric-react";
import { libraryService } from "../../services";
import { sampleActions } from "../../actions/sampleActions";
import * as formStyles from "./AddContributionForm.styles";
import * as commonStyles from "../shared/Button.styles";
import * as Constants from "../shared/Constants"
import * as Constants from "../shared/Constants";
const initialState = {
showForm: false,
errors: [],
showErrorDialog: false,
title: "",
description: "",
repository: "",
@ -36,6 +38,8 @@ class AddContributionForm extends Component {
this.onAddButtonClick = this.onAddButtonClick.bind(this);
this.onCancelButtonClick = this.onCancelButtonClick.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.onDismissErrorDialog = this.onDismissErrorDialog.bind(this);
this.validateForm = this.getFormValidationErrors.bind(this);
}
technologiesOptionsChanged(newValue) {
@ -70,19 +74,69 @@ class AddContributionForm extends Component {
this.setState({ [name]: newValue });
}
getFormValidationErrors(sample) {
let errors = [];
if (sample.title.length === 0) {
errors.push("Title cannot be empty");
}
if (sample.repository.length === 0) {
errors.push("Repository URL cannot be empty");
}
if (sample.description.length === 0) {
errors.push("Description cannot be empty");
}
if (sample.technologies.length === 0) {
errors.push("At least one technology must be selected");
}
if (sample.language.length === 0) {
errors.push("Language must be selected");
}
if (sample.solutionareas.length === 0) {
errors.push("At least one solution area must be selected");
}
return errors;
}
onLinkClick() {
this.setState({ showForm: true });
}
setErrorState(errors) {
this.setState({
errors: errors,
showErrorDialog: true
});
}
onAddButtonClick() {
libraryService
.submitNewSample(this.state)
.then(
sample => this.props.sampleSubmittedSuccess(sample),
error => console.log(error) // todo
)
.then(this.resetForm())
.catch(error => console.log(error)); // todo
const sample = {
title: this.state.title,
description: this.state.description,
repository: this.state.repository,
template: this.state.template,
technologies: this.state.technologies,
language: this.state.language,
solutionareas: this.state.solutionareas
};
const errors = this.getFormValidationErrors(sample);
if (errors.length > 0) {
this.setErrorState(errors);
return;
}
libraryService.submitNewSample(sample).then(
sample => {
this.props.sampleSubmittedSuccess(sample);
this.resetForm();
},
error => {
this.setErrorState(error);
}
);
}
onDismissErrorDialog() {
this.setState({ showErrorDialog: false });
}
onCancelButtonClick() {
@ -94,10 +148,16 @@ class AddContributionForm extends Component {
}
render() {
let technologiesOptions = Constants.technologies.map(t => ({ key: t, text: t }));
let technologiesOptions = Constants.technologies.map(t => ({
key: t,
text: t
}));
let languageOptions = Constants.languages.map(l => ({ key: l, text: l }));
let solutionAreasOptions = Constants.solutionAreas.map(s => ({ key: s, text: s }));
const { showForm } = this.state;
let solutionAreasOptions = Constants.solutionAreas.map(s => ({
key: s,
text: s
}));
let { showForm, errors, showErrorDialog } = this.state;
return (
<div className="add-contribution-container">
<div className="add-contribution-link">
@ -108,6 +168,21 @@ class AddContributionForm extends Component {
</div>
{showForm && (
<div className="contribution-form-container">
{showErrorDialog && (
<Dialog
dialogContentProps={{
title: "An error occurred!"
}}
hidden={!showErrorDialog}
onDismiss={this.onDismissErrorDialog}
>
<ul>
{errors.map((message, index) => (
<li key={index}>{message}</li>
))}
</ul>
</Dialog>
)}
<div className="input-container">
<div className="contribution-form-fields-container">
<TextField
@ -154,7 +229,6 @@ class AddContributionForm extends Component {
required={true}
options={languageOptions}
/>
<Dropdown
placeholder="Select categories for your sample"
label="Solution Area"
@ -165,7 +239,6 @@ class AddContributionForm extends Component {
multiSelect
options={solutionAreasOptions}
/>
<TextField
name="template"
label="ARM template URL"

Просмотреть файл

@ -17,16 +17,6 @@ function getAllSamples() {
}
function submitNewSample(item) {
item.type = "functionapp"; // todo - these props should come from the contribution form
item.runtimeversion = "v2";
item.language = "javascript";
if (useMockApi) {
item.id = "someid"; // id and author are set by the backend api
item.author = "msnehagup";
return Promise.resolve(item);
}
const requestOptions = {
method: "PUT",
body: JSON.stringify(item),
@ -34,7 +24,19 @@ function submitNewSample(item) {
"Content-Type": "application/json"
}
};
return fetch("/api/library", requestOptions).then(handleResponse);
return fetch("/api/library", requestOptions).then(response => {
if (response.ok) {
return response.json();
}
if (response.status === 400) {
return response.json().then(json => Promise.reject(json));
}
return response
.text()
.then(text => Promise.reject(text || response.statusText));
});
}
function updateUserSentimentStats(sentimentPayload) {

Просмотреть файл

@ -59,15 +59,11 @@ namespace ServerlessLibrary.Controllers
{
return Unauthorized();
}
if (string.IsNullOrWhiteSpace(libraryItem.Repository) || !IsValidUri(libraryItem.Repository))
{
return BadRequest("GitHub URL is mandatory, and must be a valid URL");
}
if (!string.IsNullOrWhiteSpace(libraryItem.Template) && !IsValidUri(libraryItem.Template))
var validationsErrors = ValidateLibraryItem(libraryItem);
if (validationsErrors?.Count > 0)
{
return BadRequest("ARM template link must be a valid URL");
return BadRequest(validationsErrors);
}
// assign id, created date
@ -82,6 +78,47 @@ namespace ServerlessLibrary.Controllers
return new JsonResult(libraryItem);
}
private static List<string> ValidateLibraryItem(LibraryItem libraryItem)
{
List<string> errors = new List<string>();
if (string.IsNullOrWhiteSpace(libraryItem.Title))
{
errors.Add("Title cannot be empty");
}
if (string.IsNullOrWhiteSpace(libraryItem.Repository) || !IsValidUri(libraryItem.Repository))
{
errors.Add("Repository URL must be a valid GitHub URL");
}
if (string.IsNullOrWhiteSpace(libraryItem.Description))
{
errors.Add("Description cannot be empty");
}
if (libraryItem.Technologies.Length == 0)
{
errors.Add("At least one technology must be specified");
}
if (string.IsNullOrWhiteSpace(libraryItem.Language))
{
errors.Add("Language must be specified");
}
if (libraryItem.SolutionAreas.Length == 0)
{
errors.Add("At least one solution area must be specified");
}
if (!string.IsNullOrWhiteSpace(libraryItem.Template) && !IsValidUri(libraryItem.Template))
{
errors.Add("ARM template URL must be a valid URL");
}
return errors;
}
private static bool IsValidUri(string uriString)
{
try