Editor/Window/Containers/ContainersUserInputValidation.cs (135 lines of code) (raw):
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
using System.Collections.Generic;
using System.IO;
using UnityEngine.UIElements;
using System.Text.RegularExpressions;
using System;
namespace AmazonGameLift.Editor
{
public class ContainersUserInputValidation
{
public static readonly Regex PORT_RANGE_REGEX = new Regex("^[0-9]+-[0-9]+$");
public static readonly Regex POSITIVE_INTEGER_REGEX = new Regex("^[0-9]+$");
public static readonly Regex GAME_NAME_REGEX = new Regex("^[a-zA-Z0-9-]{1,94}$");
public static readonly Regex CONTAINER_IMAGE_TAG_REGEX = new Regex("^[a-zA-Z0-9._-]{1,300}$");
public static readonly Regex DOCKER_IMAGE_ID_REGEX = new Regex("^(?:(?=[^:/]{1,253})(?!-)[a-zA-Z0-9-]{1,63}(?<!-)(?:.(?!-)[a-zA-Z0-9-]{1,63}(?<!-))*(?::[0-9]{1,5})?/)?((?![._-])(?:[a-z0-9._-]*)(?<![._-])(?:/(?![._-])[a-z0-9._-]*(?<![._-]))*)(?::(?![.-])[a-zA-Z0-9_.-]{1,128})?$");
public static readonly string DEFAULT_MEMORY_LIMIT = "4000";
public static readonly string DEFAULT_VCPU_LIMIT = "2";
public static readonly string DEFAULT_IMAGE_TAG = "unity-gamelift-plugin";
public static readonly string DEFAULT_GAME_NAME = "MyGame";
public static readonly string DEFAULT_PORT_RANGE = "33430-33440";
private readonly Dictionary<string, string> _ecrRepoNameUriMap = new Dictionary<string, string>();
private IReadOnlyDictionary<ContainersUserInputType, Label> _errorMessageMappings;
private IReadOnlyDictionary<ContainersUserInputType, VisualElement> _inputMappings;
private IReadOnlyDictionary<ContainersUserInputType, Regex> _regexMappings;
private List<ContainersUserInputType> _activeInputs;
public Action OnValidationEvent;
public ContainersUserInputValidation(IReadOnlyDictionary<ContainersUserInputType, Label> errorMessageMappings, IReadOnlyDictionary<ContainersUserInputType, VisualElement> inputMappings)
{
_errorMessageMappings = errorMessageMappings;
_inputMappings = inputMappings;
_activeInputs = new List<ContainersUserInputType>();
_regexMappings = GetRegexMappings();
}
public void SetActiveInputs(List<ContainersUserInputType> inputTypes)
{
_activeInputs.Clear();
_activeInputs.AddRange(inputTypes);
}
public void RegisterValidationCallbacks()
{
foreach (ContainersUserInputType inputType in _inputMappings.Keys)
{
var input = _inputMappings.GetValueOrDefault(inputType);
if (inputType == ContainersUserInputType.GameServerExecutableInput ||
inputType == ContainersUserInputType.GameServerFolderInput)
{
((TextField)input).RegisterValueChangedCallback(evt =>
{
ValidateInput(inputType);
OnValidationEvent?.Invoke();
});
}
else
{
input.RegisterCallback<FocusOutEvent>(evt =>
{
ValidateInput(inputType);
OnValidationEvent?.Invoke();
});
}
}
}
public bool AllInputsValid()
{
foreach (ContainersUserInputType inputType in _activeInputs)
{
if (!IsInputValid(inputType))
{
return false;
}
}
return true;
}
public bool IsInputValid(ContainersUserInputType inputType)
{
VisualElement input = _inputMappings.GetValueOrDefault(inputType);
switch (inputType)
{
//dropdowns
case ContainersUserInputType.EcrRepositoryDropdown:
case ContainersUserInputType.EcrImageDropdown:
DropdownField dropdown = (DropdownField)input;
return dropdown.value != null;
// file checking
case ContainersUserInputType.GameServerFolderInput:
TextField containerGameServerBuildInput = (TextField)input;
return !string.IsNullOrEmpty(containerGameServerBuildInput.value) &&
Directory.Exists(containerGameServerBuildInput.value);
case ContainersUserInputType.GameServerExecutableInput:
TextField containerGameServerExecutableInput = (TextField)input;
return !string.IsNullOrEmpty(containerGameServerExecutableInput.value) &&
File.Exists(containerGameServerExecutableInput.value);
// regex checking
case ContainersUserInputType.DockerImageInput:
case ContainersUserInputType.ContainerImageTagInput:
case ContainersUserInputType.ConnectionPortRangeInput:
case ContainersUserInputType.MemoryLimitInput:
case ContainersUserInputType.VcpuLimitInput:
case ContainersUserInputType.GameNameInput:
TextField textInput = (TextField)input;
Regex inputRegex = _regexMappings.GetValueOrDefault(inputType);
return !string.IsNullOrEmpty(textInput.value) && inputRegex.Match(textInput.value).Success;
default:
return false;
}
}
public void ValidateInput(ContainersUserInputType inputType)
{
Label errorLabel = _errorMessageMappings.GetValueOrDefault(inputType);
StatefulInput.ShowHide(errorLabel, !IsInputValid(inputType));
}
private Dictionary<ContainersUserInputType, Regex> GetRegexMappings()
{
var regexMappings = new Dictionary<ContainersUserInputType, Regex>
{
{ ContainersUserInputType.ConnectionPortRangeInput, PORT_RANGE_REGEX },
{ ContainersUserInputType.ContainerImageTagInput, CONTAINER_IMAGE_TAG_REGEX },
{ ContainersUserInputType.DockerImageInput, DOCKER_IMAGE_ID_REGEX },
{ ContainersUserInputType.GameNameInput, GAME_NAME_REGEX },
{ ContainersUserInputType.MemoryLimitInput, POSITIVE_INTEGER_REGEX },
{ ContainersUserInputType.VcpuLimitInput, POSITIVE_INTEGER_REGEX }
};
return regexMappings;
}
}
public enum ContainersUserInputType
{
EcrRepositoryDropdown,
EcrImageDropdown,
DockerImageInput,
GameServerFolderInput,
GameServerExecutableInput,
GameNameInput,
ConnectionPortRangeInput,
MemoryLimitInput,
VcpuLimitInput,
ContainerImageTagInput,
}
}