Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions web_platform_dx__web_features_extras.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ package web_platform_dx__web_features
// This is a temporary file until v3 lands
// Takes files generated from https://github.com/GoogleChrome/webstatus.dev/pull/1635

type FeatureDataKind string

const (
Feature FeatureDataKind = "feature"
)

type FeatureMovedDataKind string

const (
Expand Down
132 changes: 127 additions & 5 deletions workflows/steps/services/web_feature_consumer/pkg/data/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,35 @@ import (
// Parser contains the logic to parse the JSON from the web-features Github Release.
type Parser struct{}

// V3Parser contains the logic to parse the JSON from the web-features Github Release.
type V3Parser struct{}

var ErrUnexpectedFormat = errors.New("unexpected format")

var ErrUnableToProcess = errors.New("unable to process the data")

// rawWebFeaturesJSONData is used to parse the source JSON.
// rawWebFeaturesJSONDataV2 is used to parse the source JSON.
// It holds the features as raw JSON messages to be processed individually.
type rawWebFeaturesJSONDataV2 struct {
Browsers web_platform_dx__web_features.Browsers `json:"browsers"`
Groups map[string]web_platform_dx__web_features.GroupData `json:"groups"`
Snapshots map[string]web_platform_dx__web_features.SnapshotData `json:"snapshots"`
Features map[string]web_platform_dx__web_features.FeatureValue `json:"features"`
}

// rawWebFeaturesJSONDataV3 is used to parse the source JSON.
// It holds the features as raw JSON messages to be processed individually.
type rawWebFeaturesJSONData struct {
type rawWebFeaturesJSONDataV3 struct {
Browsers web_platform_dx__web_features.Browsers `json:"browsers"`
Groups map[string]web_platform_dx__web_features.GroupData `json:"groups"`
Snapshots map[string]web_platform_dx__web_features.SnapshotData `json:"snapshots"`
// TODO: When we move to v3, we will change Features to being json.RawMessage
Features map[string]web_platform_dx__web_features.FeatureValue `json:"features"`
Features json.RawMessage `json:"features"`
}

// featureKindPeek is a small helper struct to find the discriminator value in V3.
type featureKindPeek struct {
Kind string `json:"kind"`
}

// Parse expects the raw bytes for a map of string to
Expand All @@ -47,7 +64,7 @@ type rawWebFeaturesJSONData struct {
// It will consume the readcloser and close it.
func (p Parser) Parse(in io.ReadCloser) (*webdxfeaturetypes.ProcessedWebFeaturesData, error) {
defer in.Close()
var source rawWebFeaturesJSONData
var source rawWebFeaturesJSONDataV2
decoder := json.NewDecoder(in)
err := decoder.Decode(&source)
if err != nil {
Expand All @@ -59,7 +76,28 @@ func (p Parser) Parse(in io.ReadCloser) (*webdxfeaturetypes.ProcessedWebFeatures
return processedData, nil
}

func postProcess(data *rawWebFeaturesJSONData) *webdxfeaturetypes.ProcessedWebFeaturesData {
// Parse expects the raw bytes for a map of string to
// https://github.com/web-platform-dx/web-features/blob/main/schemas/defs.schema.json
// The string is the feature ID.
// It will consume the readcloser and close it.
func (p V3Parser) Parse(in io.ReadCloser) (*webdxfeaturetypes.ProcessedWebFeaturesData, error) {
defer in.Close()
var source rawWebFeaturesJSONDataV3
decoder := json.NewDecoder(in)
err := decoder.Decode(&source)
if err != nil {
return nil, errors.Join(ErrUnexpectedFormat, err)
}

processedData, err := postProcessV3(&source)
if err != nil {
return nil, errors.Join(ErrUnableToProcess, err)
}

return processedData, nil
}

func postProcess(data *rawWebFeaturesJSONDataV2) *webdxfeaturetypes.ProcessedWebFeaturesData {
featureKinds := postProcessFeatureValue(data.Features)

return &webdxfeaturetypes.ProcessedWebFeaturesData{
Expand All @@ -70,6 +108,90 @@ func postProcess(data *rawWebFeaturesJSONData) *webdxfeaturetypes.ProcessedWebFe
}
}

func postProcessV3(data *rawWebFeaturesJSONDataV3) (*webdxfeaturetypes.ProcessedWebFeaturesData, error) {
featureKinds, err := postProcessFeatureValueV3(data.Features)
if err != nil {
return nil, err
}

return &webdxfeaturetypes.ProcessedWebFeaturesData{
Browsers: data.Browsers,
Groups: data.Groups,
Snapshots: data.Snapshots,
Features: featureKinds,
}, nil
}

func postProcessFeatureValueV3(data json.RawMessage) (*webdxfeaturetypes.FeatureKinds, error) {
featureKinds := webdxfeaturetypes.FeatureKinds{
Data: nil,
Moved: nil,
Split: nil,
}

featureRawMessageMap := make(map[string]json.RawMessage)

err := json.Unmarshal(data, &featureRawMessageMap)
if err != nil {
return nil, err
}

for id, rawFeature := range featureRawMessageMap {
// Peek inside the raw JSON to find the "kind"
var peek featureKindPeek
if err := json.Unmarshal(rawFeature, &peek); err != nil {
// Skip or log features that don't have a 'kind' field
continue
}

// Switch on the explicit "kind" to unmarshal into the correct type
switch peek.Kind {
case string(web_platform_dx__web_features.Feature):
if featureKinds.Data == nil {
featureKinds.Data = make(map[string]web_platform_dx__web_features.FeatureValue)
}
var value web_platform_dx__web_features.FeatureValue
if err := json.Unmarshal(rawFeature, &value); err != nil {
return nil, err
}
// Run your existing post-processing logic
featureKinds.Data[id] = web_platform_dx__web_features.FeatureValue{
Caniuse: postProcessStringOrStringArray(value.Caniuse),
CompatFeatures: value.CompatFeatures,
Description: value.Description,
DescriptionHTML: value.DescriptionHTML,
Group: postProcessStringOrStringArray(value.Group),
Name: value.Name,
Snapshot: postProcessStringOrStringArray(value.Snapshot),
Spec: postProcessStringOrStringArray(value.Spec),
Status: postProcessStatus(value.Status),
Discouraged: value.Discouraged,
}

case string(web_platform_dx__web_features.Moved):
if featureKinds.Moved == nil {
featureKinds.Moved = make(map[string]web_platform_dx__web_features.FeatureMovedData)
}
var value web_platform_dx__web_features.FeatureMovedData
if err := json.Unmarshal(rawFeature, &value); err != nil {
return nil, err
}
featureKinds.Moved[id] = value

case string(web_platform_dx__web_features.Split):
if featureKinds.Split == nil {
featureKinds.Split = make(map[string]web_platform_dx__web_features.FeatureSplitData)
}
var value web_platform_dx__web_features.FeatureSplitData
if err := json.Unmarshal(rawFeature, &value); err != nil {
return nil, err
}
featureKinds.Split[id] = value
}
}

return &featureKinds, nil
}
func postProcessFeatureValue(
data map[string]web_platform_dx__web_features.FeatureValue) *webdxfeaturetypes.FeatureKinds {
featureKinds := webdxfeaturetypes.FeatureKinds{
Expand Down
Loading