web ui: index and basic details page, api refactoring

This commit is contained in:
2023-04-04 21:51:45 +03:00
parent 2a8b94136f
commit f47dbefb67
21 changed files with 821 additions and 58 deletions

View File

@@ -534,14 +534,20 @@ func (s *Page) encodeFields(e *jx.Encoder) {
e.FieldStart("status")
s.Status.Encode(e)
}
{
e.FieldStart("meta")
s.Meta.Encode(e)
}
}
var jsonFieldsNameOfPage = [5]string{
var jsonFieldsNameOfPage = [6]string{
0: "id",
1: "url",
2: "created",
3: "formats",
4: "status",
5: "meta",
}
// Decode decodes Page from json.
@@ -617,6 +623,16 @@ func (s *Page) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"status\"")
}
case "meta":
requiredBitSet[0] |= 1 << 5
if err := func() error {
if err := s.Meta.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"meta\"")
}
default:
return d.Skip()
}
@@ -627,7 +643,7 @@ func (s *Page) Decode(d *jx.Decoder) error {
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00011111,
0b00111111,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
@@ -673,6 +689,138 @@ func (s *Page) UnmarshalJSON(data []byte) error {
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *PageMeta) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *PageMeta) encodeFields(e *jx.Encoder) {
{
e.FieldStart("title")
e.Str(s.Title)
}
{
e.FieldStart("description")
e.Str(s.Description)
}
{
if s.Error.Set {
e.FieldStart("error")
s.Error.Encode(e)
}
}
}
var jsonFieldsNameOfPageMeta = [3]string{
0: "title",
1: "description",
2: "error",
}
// Decode decodes PageMeta from json.
func (s *PageMeta) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode PageMeta to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "title":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Str()
s.Title = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"title\"")
}
case "description":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.Str()
s.Description = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"description\"")
}
case "error":
if err := func() error {
s.Error.Reset()
if err := s.Error.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"error\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode PageMeta")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000011,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfPageMeta) {
name = jsonFieldsNameOfPageMeta[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *PageMeta) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *PageMeta) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *PageWithResults) Encode(e *jx.Encoder) {
e.ObjStart()
@@ -711,6 +859,11 @@ func (s *PageWithResults) encodeFields(e *jx.Encoder) {
e.FieldStart("status")
s.Status.Encode(e)
}
{
e.FieldStart("meta")
s.Meta.Encode(e)
}
{
e.FieldStart("results")
@@ -722,13 +875,14 @@ func (s *PageWithResults) encodeFields(e *jx.Encoder) {
}
}
var jsonFieldsNameOfPageWithResults = [6]string{
var jsonFieldsNameOfPageWithResults = [7]string{
0: "id",
1: "url",
2: "created",
3: "formats",
4: "status",
5: "results",
5: "meta",
6: "results",
}
// Decode decodes PageWithResults from json.
@@ -804,8 +958,18 @@ func (s *PageWithResults) Decode(d *jx.Decoder) error {
}(); err != nil {
return errors.Wrap(err, "decode field \"status\"")
}
case "results":
case "meta":
requiredBitSet[0] |= 1 << 5
if err := func() error {
if err := s.Meta.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"meta\"")
}
case "results":
requiredBitSet[0] |= 1 << 6
if err := func() error {
s.Results = make([]Result, 0)
if err := d.Arr(func(d *jx.Decoder) error {
@@ -832,7 +996,7 @@ func (s *PageWithResults) Decode(d *jx.Decoder) error {
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00111111,
0b01111111,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
@@ -878,6 +1042,138 @@ func (s *PageWithResults) UnmarshalJSON(data []byte) error {
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *PageWithResultsMeta) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *PageWithResultsMeta) encodeFields(e *jx.Encoder) {
{
e.FieldStart("title")
e.Str(s.Title)
}
{
e.FieldStart("description")
e.Str(s.Description)
}
{
if s.Error.Set {
e.FieldStart("error")
s.Error.Encode(e)
}
}
}
var jsonFieldsNameOfPageWithResultsMeta = [3]string{
0: "title",
1: "description",
2: "error",
}
// Decode decodes PageWithResultsMeta from json.
func (s *PageWithResultsMeta) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode PageWithResultsMeta to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "title":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Str()
s.Title = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"title\"")
}
case "description":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.Str()
s.Description = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"description\"")
}
case "error":
if err := func() error {
s.Error.Reset()
if err := s.Error.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"error\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode PageWithResultsMeta")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000011,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfPageWithResultsMeta) {
name = jsonFieldsNameOfPageWithResultsMeta[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *PageWithResultsMeta) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *PageWithResultsMeta) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode encodes Pages as json.
func (s Pages) Encode(e *jx.Encoder) {
unwrapped := []Page(s)

View File

@@ -324,6 +324,7 @@ type Page struct {
Created time.Time `json:"created"`
Formats []Format `json:"formats"`
Status Status `json:"status"`
Meta PageMeta `json:"meta"`
}
// GetID returns the value of ID.
@@ -351,6 +352,11 @@ func (s *Page) GetStatus() Status {
return s.Status
}
// GetMeta returns the value of Meta.
func (s *Page) GetMeta() PageMeta {
return s.Meta
}
// SetID sets the value of ID.
func (s *Page) SetID(val uuid.UUID) {
s.ID = val
@@ -376,17 +382,59 @@ func (s *Page) SetStatus(val Status) {
s.Status = val
}
// SetMeta sets the value of Meta.
func (s *Page) SetMeta(val PageMeta) {
s.Meta = val
}
func (*Page) addPageRes() {}
type PageMeta struct {
Title string `json:"title"`
Description string `json:"description"`
Error OptString `json:"error"`
}
// GetTitle returns the value of Title.
func (s *PageMeta) GetTitle() string {
return s.Title
}
// GetDescription returns the value of Description.
func (s *PageMeta) GetDescription() string {
return s.Description
}
// GetError returns the value of Error.
func (s *PageMeta) GetError() OptString {
return s.Error
}
// SetTitle sets the value of Title.
func (s *PageMeta) SetTitle(val string) {
s.Title = val
}
// SetDescription sets the value of Description.
func (s *PageMeta) SetDescription(val string) {
s.Description = val
}
// SetError sets the value of Error.
func (s *PageMeta) SetError(val OptString) {
s.Error = val
}
// Merged schema.
// Ref: #/components/schemas/pageWithResults
type PageWithResults struct {
ID uuid.UUID `json:"id"`
URL string `json:"url"`
Created time.Time `json:"created"`
Formats []Format `json:"formats"`
Status Status `json:"status"`
Results []Result `json:"results"`
ID uuid.UUID `json:"id"`
URL string `json:"url"`
Created time.Time `json:"created"`
Formats []Format `json:"formats"`
Status Status `json:"status"`
Meta PageWithResultsMeta `json:"meta"`
Results []Result `json:"results"`
}
// GetID returns the value of ID.
@@ -414,6 +462,11 @@ func (s *PageWithResults) GetStatus() Status {
return s.Status
}
// GetMeta returns the value of Meta.
func (s *PageWithResults) GetMeta() PageWithResultsMeta {
return s.Meta
}
// GetResults returns the value of Results.
func (s *PageWithResults) GetResults() []Result {
return s.Results
@@ -444,6 +497,11 @@ func (s *PageWithResults) SetStatus(val Status) {
s.Status = val
}
// SetMeta sets the value of Meta.
func (s *PageWithResults) SetMeta(val PageWithResultsMeta) {
s.Meta = val
}
// SetResults sets the value of Results.
func (s *PageWithResults) SetResults(val []Result) {
s.Results = val
@@ -451,6 +509,42 @@ func (s *PageWithResults) SetResults(val []Result) {
func (*PageWithResults) getPageRes() {}
type PageWithResultsMeta struct {
Title string `json:"title"`
Description string `json:"description"`
Error OptString `json:"error"`
}
// GetTitle returns the value of Title.
func (s *PageWithResultsMeta) GetTitle() string {
return s.Title
}
// GetDescription returns the value of Description.
func (s *PageWithResultsMeta) GetDescription() string {
return s.Description
}
// GetError returns the value of Error.
func (s *PageWithResultsMeta) GetError() OptString {
return s.Error
}
// SetTitle sets the value of Title.
func (s *PageWithResultsMeta) SetTitle(val string) {
s.Title = val
}
// SetDescription sets the value of Description.
func (s *PageWithResultsMeta) SetDescription(val string) {
s.Description = val
}
// SetError sets the value of Error.
func (s *PageWithResultsMeta) SetError(val OptString) {
s.Error = val
}
type Pages []Page
// Ref: #/components/schemas/result