Files
holos/internal/strings/pascal_case.go
Jeff McCune 9a2773c618 (#171) Refactor API to use FieldMasks
This patch refactors the API following the [API Best Practices][api]
documentation.  The UpdatePlatform method is modeled after a mutating
operation described [by Netflix][nflx] instead of using a REST resource
representation.  This makes it much easier to iterate over the fields
that need to be updated as the PlatformUpdateOperation is a flat data
structure while a Platform resource may have nested fields.  Nested
fields are more complicated and less clear to handle with a FieldMask.

This patch also adds a snapckbar message on save.  Previously, the save
button didn't give any indication of success or failure.  This patch
fixes the problem by adding a snackbar message that pop up at the bottom
of the screen nicely.

When the snackbar message is dismissed or times out the save button is
re-enabled.

[api]: https://protobuf.dev/programming-guides/api/
[nflx]: https://netflixtechblog.com/practical-api-design-at-netflix-part-2-protobuf-fieldmask-for-mutation-operations-2e75e1d230e4

Examples:

FieldMask for ListPlatforms

```
grpcurl -H "x-oidc-id-token: $(holos token)" -d @ ${HOLOS_SERVER##*/} holos.platform.v1alpha1.PlatformService.ListPlatforms <<EOF
{
  "org_id": "018f36fb-e3f7-7f7f-a1c5-c85fb735d215",
  "field_mask": { "paths": ["id","name"] }
}
EOF
```

```json
{
 "platforms": [
   {
     "id": "018f36fb-e3ff-7f7f-a5d1-7ca2bf499e94",
     "name": "bare"
   },
   {
     "id": "018f6b06-9e57-7223-91a9-784e145d998c",
     "name": "gary"
   },
   {
     "id": "018f6b06-9e53-7223-8ae1-1ad53d46b158",
     "name": "jeff"
   },
   {
     "id": "018f6b06-9e5b-7223-8b8b-ea62618e8200",
     "name": "nate"
   }
 ]
}
```

Closes: #171
2024-05-13 16:20:20 -07:00

74 lines
2.7 KiB
Go

// Copyright 2010 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// - Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// Original license: https://github.com/golang/protobuf/blob/master/LICENSE
// Original CamelCase func: https://github.com/golang/protobuf/blob/master/protoc-gen-go/generator/generator.go#L2648
package strings
// PascalCase returns the PascalCased name.
// If there is an interior underscore followed by a lower case letter,
// drop the underscore and convert the letter to upper case.
// There is a remote possibility of this rewrite causing a name collision,
// but it's so remote we're prepared to pretend it's nonexistent - since the
// C++ generator lowercases names, it's extremely unlikely to have two fields
// with different capitalizations.
// In short, _my_field_name_2 becomes XMyFieldName_2.
func PascalCase(s string) string {
if s == "" {
return ""
}
t := make([]byte, 0, 32)
i := 0
if s[0] == '_' {
// Need a capital letter; drop the '_'.
t = append(t, 'X')
i++
}
return string(append(t, lookupAndReplacePascalCaseWords(s, i)...))
}
// lookupAndReplacePascalCaseWords lookups for words in the string starting in position i
// and replaces the snake case format to PascalCase.
// Invariant: if the next letter is lower case, it must be converted
// to upper case.
// That is, we process a word at a time, where words are marked by _ or
// upper case letter. Digits are treated as words.
func lookupAndReplacePascalCaseWords(s string, i int) []byte {
t := make([]byte, 0, 32)
for ; i < len(s); i++ {
c := s[i]
if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
continue // Skip the underscore in s.
}
if isASCIIDigit(c) {
t = append(t, c)
continue
}
// Assume we have a letter now - if not, it's a bogus identifier.
// The next word is a sequence of characters that must start upper case.
if isASCIILower(c) {
c ^= ' ' // Make it a capital letter.
}
t = append(t, c) // Guaranteed not lower case.
// Accept lower case sequence that follows.
t, i = appendLowercaseSequence(s, i, t)
}
return t
}