frontend/common/uris.cc (187 lines of code) (raw):
//
// Copyright 2020 Google LLC
//
// 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.
//
#include "frontend/common/uris.h"
#include <memory>
#include <string>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "common/errors.h"
#include "common/limits.h"
#include "re2/re2.h"
namespace google {
namespace spanner {
namespace emulator {
namespace {
bool ConsumeResource(const absl::string_view expected_prefix,
absl::string_view* resource_uri,
absl::string_view* resource_id) {
if (!absl::ConsumePrefix(resource_uri, expected_prefix)) {
return false;
}
auto pos = resource_uri->find('/');
*resource_id = resource_uri->substr(0, pos);
absl::ConsumePrefix(resource_uri, absl::StrCat(*resource_id, "/"));
return true;
}
bool ConsumeProject(absl::string_view* resource_uri,
absl::string_view* project_id) {
return ConsumeResource("projects/", resource_uri, project_id);
}
bool ConsumeInstanceConfig(absl::string_view* resource_uri,
absl::string_view* instance_config_id) {
return ConsumeResource("instanceConfigs/", resource_uri, instance_config_id);
}
bool ConsumeInstance(absl::string_view* resource_uri,
absl::string_view* instance_id) {
return ConsumeResource("instances/", resource_uri, instance_id);
}
bool ConsumeDatabase(absl::string_view* resource_uri,
absl::string_view* database_id) {
return ConsumeResource("databases/", resource_uri, database_id);
}
bool ConsumeSession(absl::string_view* resource_uri,
absl::string_view* session_id) {
return ConsumeResource("sessions/", resource_uri, session_id);
}
bool ConsumeOperation(absl::string_view* resource_uri,
absl::string_view* operation_id) {
return ConsumeResource("operations/", resource_uri, operation_id);
}
} // namespace
absl::Status ParseProjectUri(absl::string_view resource_uri,
absl::string_view* project_id) {
if (!ConsumeProject(&resource_uri, project_id)) {
return error::InvalidProjectURI(resource_uri);
}
return absl::OkStatus();
}
absl::Status ParseInstanceConfigUri(absl::string_view resource_uri,
absl::string_view* project_id,
absl::string_view* instance_config_id) {
if (!ConsumeProject(&resource_uri, project_id)) {
return error::InvalidProjectURI(resource_uri);
}
if (!ConsumeInstanceConfig(&resource_uri, instance_config_id)) {
return error::InvalidInstanceConfigURI(resource_uri);
}
return absl::OkStatus();
}
absl::Status ParseInstanceUri(absl::string_view resource_uri,
absl::string_view* project_id,
absl::string_view* instance_id) {
if (!ConsumeProject(&resource_uri, project_id)) {
return error::InvalidProjectURI(resource_uri);
}
if (!ConsumeInstance(&resource_uri, instance_id)) {
return error::InvalidInstanceURI(resource_uri);
}
return absl::OkStatus();
}
absl::Status ValidateInstanceId(absl::string_view instance_id) {
static LazyRE2 instance_id_matcher{"[a-z][-a-z0-9]*[a-z0-9]"};
if (instance_id.size() < limits::kMinInstanceNameLength ||
instance_id.size() > limits::kMaxInstanceNameLength ||
!RE2::FullMatch(instance_id, *instance_id_matcher)) {
return error::InvalidInstanceName(instance_id);
}
return absl::OkStatus();
}
absl::Status ParseDatabaseUri(absl::string_view resource_uri,
absl::string_view* project_id,
absl::string_view* instance_id,
absl::string_view* database_id) {
if (!ConsumeProject(&resource_uri, project_id)) {
return error::InvalidProjectURI(resource_uri);
}
if (!ConsumeInstance(&resource_uri, instance_id)) {
return error::InvalidInstanceURI(resource_uri);
}
if (!ConsumeDatabase(&resource_uri, database_id)) {
return error::InvalidDatabaseURI(resource_uri);
}
return absl::OkStatus();
}
absl::Status ValidateDatabaseId(absl::string_view database_id) {
static LazyRE2 database_id_matcher{"[a-z][-a-z0-9_]*[a-z0-9]"};
if (database_id.size() < limits::kMinDatabaseNameLength ||
database_id.size() > limits::kMaxDatabaseNameLength ||
!RE2::FullMatch(database_id, *database_id_matcher)) {
return error::InvalidDatabaseName(database_id);
}
return absl::OkStatus();
}
absl::Status ParseSessionUri(absl::string_view resource_uri,
absl::string_view* project_id,
absl::string_view* instance_id,
absl::string_view* database_id,
absl::string_view* session_id) {
if (!ConsumeProject(&resource_uri, project_id)) {
return error::InvalidProjectURI(resource_uri);
}
if (!ConsumeInstance(&resource_uri, instance_id)) {
return error::InvalidInstanceURI(resource_uri);
}
if (!ConsumeDatabase(&resource_uri, database_id)) {
return error::InvalidDatabaseURI(resource_uri);
}
if (!ConsumeSession(&resource_uri, session_id)) {
return error::InvalidSessionURI(resource_uri);
}
return absl::OkStatus();
}
absl::Status ParseOperationUri(absl::string_view operation_uri,
std::shared_ptr<std::string> resource_uri,
absl::string_view* operation_id) {
absl::string_view project_id, instance_id, database_id;
if (!ConsumeProject(&operation_uri, &project_id)) {
return error::InvalidProjectURI(operation_uri);
}
if (!ConsumeInstance(&operation_uri, &instance_id)) {
return error::InvalidInstanceURI(operation_uri);
}
// Operations may be performed on an instance, or a database. Call
// ConsumeDatabase to remove "databases/<database_id>" if exists. Proceed
// regardless of the returned value.
if (ConsumeDatabase(&operation_uri, &database_id)) {
resource_uri = std::make_shared<std::string>(
MakeDatabaseUri(MakeInstanceUri(project_id, instance_id), database_id));
} else {
resource_uri =
std::make_shared<std::string>(MakeInstanceUri(project_id, instance_id));
}
if (!ConsumeOperation(&operation_uri, operation_id)) {
return error::InvalidOperationURI(operation_uri);
}
return absl::OkStatus();
}
std::string MakeProjectUri(absl::string_view project_id) {
return absl::StrCat("projects/", project_id);
}
std::string MakeInstanceConfigUri(absl::string_view project_id,
absl::string_view instance_config_id) {
return absl::StrCat("projects/", project_id, "/instanceConfigs/",
instance_config_id);
}
std::string MakeInstanceUri(absl::string_view project_id,
absl::string_view instance_id) {
return absl::StrCat("projects/", project_id, "/instances/", instance_id);
}
std::string MakeDatabaseUri(absl::string_view instance_uri,
absl::string_view database_id) {
return absl::StrCat(instance_uri, "/databases/", database_id);
}
std::string MakeSessionUri(absl::string_view database_uri,
absl::string_view session_id) {
return absl::StrCat(database_uri, "/sessions/", session_id);
}
std::string MakeOperationUri(absl::string_view resource_uri,
absl::string_view operation_id) {
return absl::StrCat(resource_uri, "/operations/", operation_id);
}
bool IsValidOperationId(absl::string_view operation_id) {
if (operation_id.length() < limits::kDatabaseOpIdMinLength ||
operation_id.length() > limits::kDatabaseOpIdMaxLength) {
return false;
}
static LazyRE2 re{"[a-z][a-z0-9_]*"};
return RE2::FullMatch(operation_id, *re);
}
} // namespace emulator
} // namespace spanner
} // namespace google