// Schema returns the configuration file schema
func Schema() schematypes.Object {
	transformations := []string{}
	for name := range Providers() {
		transformations = append(transformations, name)
	}
	s := schematypes.Object{
		MetaData: schematypes.MetaData{
			Title:       "Worker Configuration",
			Description: `Initial configuration and transformations to run.`,
		},
		Properties: schematypes.Properties{
			"transforms": schematypes.Array{
				MetaData: schematypes.MetaData{
					Title:       "Configuration Transformations",
					Description: "Ordered list of transformations to run on the config.",
				},
				Items: schematypes.StringEnum{
					Options: transformations,
				},
			},
			"config": worker.ConfigSchema(),
		},
		Required: []string{"config"},
	}
	return s
}
// Load configuration from YAML config object.
func Load(data []byte) (map[string]interface{}, error) {
	// Parse config file
	var config interface{}
	err := yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse YAML config, error: %s", err)
	}
	// This fixes obscurities in yaml.Unmarshal where it generates
	// map[interface{}]interface{} instead of map[string]interface{}
	// credits: https://github.com/go-yaml/yaml/issues/139#issuecomment-220072190
	config = convertSimpleJSONTypes(config)

	// Extract transforms and config
	c, ok := config.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("Expected top-level config value to be an object")
	}
	result, ok := c["config"].(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("Expected 'config' property to be an object")
	}

	// Ensure that we have a simple JSON compatible structure
	if err := jsonCompatTypes(result); err != nil {
		panic(fmt.Sprintf("YAML loaded wrong types, error: %s", err))
	}

	// Apply transforms
	if ct, ok := c["transforms"]; ok {
		var transforms []string
		err := schematypes.MustMap(Schema().Properties["transforms"], ct, &transforms)
		if err != nil {
			return nil, fmt.Errorf("'transforms' schema violated, error: %s", err)
		}

		providers := Providers()
		for _, t := range transforms {
			provider, ok := providers[t]
			if !ok {
				return nil, fmt.Errorf("Unknown config transformation: %s", t)
			}
			if err := provider.Transform(result); err != nil {
				return nil, fmt.Errorf("Config transformation: %s failed error: %s",
					t, err)
			}

			// Ensure that transform only injects simple JSON compatible types
			if err := jsonCompatTypes(result); err != nil {
				panic(fmt.Sprintf("%s injected wrong types, error: %s", t, err))
			}
		}
	}

	// Filter out keys that aren't in the config schema...
	// This way extra keys can be used to provide options for the
	// transformations, like "secrets" which will use the secretsBaseUrl if
	// present in the configuration.
	worker.ConfigSchema().Filter(result)

	// Validate against worker schema
	if err := worker.ConfigSchema().Validate(result); err != nil {
		return nil, err
	}

	return result, nil
}