Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
H
helm3
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
helm3
Commits
5ed1dc25
Commit
5ed1dc25
authored
Mar 23, 2016
by
Dave Cunningham
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Write some new unit tests for expandybird
parent
69f31b9e
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
663 additions
and
135 deletions
+663
-135
expander.go
cmd/expandybird/expander/expander.go
+6
-1
expander_test.go
cmd/expandybird/expander/expander_test.go
+657
-134
No files found.
cmd/expandybird/expander/expander.go
View file @
5ed1dc25
...
...
@@ -58,6 +58,11 @@ func (e *expander) ExpandChart(request *common.ExpansionRequest) (*common.Expans
chartInv
:=
request
.
ChartInvocation
chartFile
:=
request
.
Chart
.
Chartfile
chartMembers
:=
request
.
Chart
.
Members
if
chartInv
.
Type
!=
chartFile
.
Name
{
return
nil
,
fmt
.
Errorf
(
"Request chart invocation does not match provided chart"
)
}
schemaName
:=
chartInv
.
Type
+
".schema"
if
chartFile
.
Expander
==
nil
{
...
...
@@ -89,7 +94,7 @@ func (e *expander) ExpandChart(request *common.ExpansionRequest) (*common.Expans
message
:=
fmt
.
Sprintf
(
"The entrypoint in the chart.yaml cannot be found: %s"
,
chartFile
.
Expander
.
Entrypoint
)
return
nil
,
fmt
.
Errorf
(
"%s: %s"
,
chartInv
.
Name
,
message
)
}
if
schemaIndex
==
-
1
{
if
chartFile
.
Schema
!=
""
&&
schemaIndex
==
-
1
{
message
:=
fmt
.
Sprintf
(
"The schema in the chart.yaml cannot be found: %s"
,
chartFile
.
Schema
)
return
nil
,
fmt
.
Errorf
(
"%s: %s"
,
chartInv
.
Name
,
message
)
}
...
...
cmd/expandybird/expander/expander_test.go
View file @
5ed1dc25
...
...
@@ -16,168 +16,691 @@ limitations under the License.
package
expander
/*
import
(
"fmt"
"io"
"io/ioutil"
"os"
"path"
"reflect"
"runtime"
"strings"
"testing"
"github.com/kubernetes/helm/pkg/chart"
"github.com/kubernetes/helm/pkg/common"
"github.com/kubernetes/helm/pkg/util"
)
var importFileNames = []string{
"../test/replicatedservice.py",
}
var validFileName = "../test/ValidContent.yaml"
var outputFileName = "../test/ExpectedOutput.yaml"
var archiveFileName = "../test/TestArchive.tar"
var
expanderName
=
"../../../expansion/expansion.py"
type
ExpanderT
estCase struct {
type
t
estCase
struct
{
Description
string
TemplateFileName string
ImportFileNames []string
Request
*
common
.
ExpansionRequest
ExpectedResponse
*
common
.
ExpansionResponse
ExpectedError
string
}
func (etc *ExpanderTestCase) GetTemplate(t *testing.T) *common.Template {
template, err := util.NewTemplateFromFileNames(etc.TemplateFileName, etc.ImportFileNames)
if err != nil {
t.Fatalf("cannot create template for test case '%s': %s", etc.Description, err)
}
// content provides an easy way to provide file content verbatim in tests.
func
content
(
lines
[]
string
)
[]
byte
{
return
[]
byte
(
strings
.
Join
(
lines
,
"
\n
"
)
+
"
\n
"
)
}
return template
// funcName returns the name of the calling function.
func
funcName
()
string
{
pc
,
_
,
_
,
_
:=
runtime
.
Caller
(
1
)
return
runtime
.
FuncForPC
(
pc
)
.
Name
()
}
func GetOutputString(t *testing.T, description string) string {
output, err := ioutil.ReadFile(outputFileName)
func
testExpansion
(
t
*
testing
.
T
,
req
*
common
.
ExpansionRequest
,
expResponse
*
common
.
ExpansionResponse
,
expError
string
)
{
backend
:=
NewExpander
(
expanderName
)
response
,
err
:=
backend
.
ExpandChart
(
req
)
if
err
!=
nil
{
t.Fatalf("cannot read output file for test case '%s': %s", description, err)
message
:=
err
.
Error
()
if
expResponse
!=
nil
||
!
strings
.
Contains
(
message
,
expError
)
{
t
.
Fatalf
(
"unexpected error: %s
\n
"
,
message
)
}
}
else
{
if
expResponse
==
nil
{
t
.
Fatalf
(
"expected error did not occur: %s
\n
"
,
expError
)
}
if
!
reflect
.
DeepEqual
(
response
,
expResponse
)
{
message
:=
fmt
.
Sprintf
(
"want:
\n
%s
\n
have:
\n
%s
\n
"
,
expResponse
,
response
)
t
.
Fatalf
(
"output mismatch:
\n
%s
\n
"
,
message
)
}
}
}
return string(output)
var
pyExpander
=
&
chart
.
Expander
{
Name
:
"ExpandyBird"
,
Entrypoint
:
"templates/main.py"
,
}
func expandAndVerifyOutput(t *testing.T, actualOutput, description string) {
actualResult, err := NewExpansionResult(actualOutput)
if err != nil {
t.Fatalf("error in test case '%s': %s\n", description, err)
}
var
jinjaExpander
=
&
chart
.
Expander
{
Name
:
"ExpandyBird"
,
Entrypoint
:
"templates/main.jinja"
,
}
expectedOutput := GetOutputString(t, description)
expectedResult, err := NewExpansionResult(expectedOutput)
if err != nil {
t.Fatalf("error in test case '%s': %s\n", description, err)
}
func
TestEmptyJinja
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"resources:"
}),
},
},
},
},
&
common
.
ExpansionResponse
{
Resources
:
[]
interface
{}{},
},
""
,
// Error
)
}
if !reflect.DeepEqual(actualResult, expectedResult) {
message := fmt.Sprintf("want:\n%s\nhave:\n%s\n", expectedOutput, actualOutput)
t.Fatalf("error in test case '%s':\n%s\n", description, message)
}
func
TestEmptyPython
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
pyExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.py"
,
Content
:
content
([]
string
{
"def GenerateConfig(ctx):"
,
" return 'resources:'"
,
}),
},
},
},
},
&
common
.
ExpansionResponse
{
Resources
:
[]
interface
{}{},
},
""
,
// Error
)
}
func testExpandTemplateFromFile(t *testing.T, fileName, baseName string, importFileNames []string,
constructor func(string, io.Reader, []string) (*common.Template, error)) {
file, err := os.Open(fileName)
if err != nil {
t.Fatalf("cannot open file %s: %s", fileName, err)
}
func
TestSimpleJinja
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"resources:"
,
"- name: foo"
,
" type: bar"
,
}),
},
},
},
},
&
common
.
ExpansionResponse
{
Resources
:
[]
interface
{}{
map
[
string
]
interface
{}{
"name"
:
"foo"
,
"type"
:
"bar"
,
},
},
},
""
,
// Error
)
}
template, err := constructor(baseName, file, importFileNames)
if err != nil {
t.Fatalf("cannot create template from file %s: %s", fileName, err)
}
func
TestSimplePython
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
pyExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.py"
,
Content
:
content
([]
string
{
"def GenerateConfig(ctx):"
,
" return '''resources:"
,
"- name: foo"
,
" type: bar"
,
"'''"
,
}),
},
},
},
},
&
common
.
ExpansionResponse
{
Resources
:
[]
interface
{}{
map
[
string
]
interface
{}{
"name"
:
"foo"
,
"type"
:
"bar"
,
},
},
},
""
,
// Error
)
}
backend := NewExpander(expanderName)
actualOutput, err := backend.ExpandTemplate(template)
if err != nil {
t.Fatalf("cannot expand template from file %s: %s", fileName, err)
}
func
TestPropertiesJinja
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
Properties
:
map
[
string
]
interface
{}{
"prop1"
:
3.0
,
"prop2"
:
"foo"
,
},
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"resources:"
,
"- name: foo"
,
" type: {{ properties.prop2 }}"
,
" properties:"
,
" something: {{ properties.prop1 }}"
,
}),
},
},
},
},
&
common
.
ExpansionResponse
{
Resources
:
[]
interface
{}{
map
[
string
]
interface
{}{
"name"
:
"foo"
,
"properties"
:
map
[
string
]
interface
{}{
"something"
:
3.0
,
},
"type"
:
"foo"
,
},
},
},
""
,
// Error
)
}
description := fmt.Sprintf("test expand template from file: %s", fileName)
expandAndVerifyOutput(t, actualOutput, description)
}
func TestExpandTemplateFromReader(t *testing.T) {
baseName := path.Base(validFileName)
testExpandTemplateFromFile(t, validFileName, baseName, importFileNames, util.NewTemplateFromReader)
}
func TestExpandTemplateFromArchive(t *testing.T) {
baseName := path.Base(validFileName)
testExpandTemplateFromFile(t, archiveFileName, baseName, nil, util.NewTemplateFromArchive)
}
var ExpanderTestCases = []ExpanderTestCase{
{
"expect error for invalid file name",
"../test/InvalidFileName.yaml",
importFileNames,
"ExpansionError: Exception",
},
{
"expect error for invalid property",
"../test/InvalidProperty.yaml",
importFileNames,
"ExpansionError: Exception",
},
{
"expect error for malformed content",
"../test/MalformedContent.yaml",
importFileNames,
"ExpansionError: Error parsing YAML: mapping values are not allowed here",
},
{
"expect error for missing imports",
"../test/MissingImports.yaml",
importFileNames,
"ExpansionError: Exception",
},
{
"expect error for missing resource name",
"../test/MissingResourceName.yaml",
importFileNames,
"ExpansionError: Resource does not have a name",
},
{
"expect error for missing type name",
"../test/MissingTypeName.yaml",
importFileNames,
"ExpansionError: Resource does not have type defined",
},
{
"expect success",
validFileName,
importFileNames,
"",
},
}
func TestExpandTemplate(t *testing.T) {
backend := NewExpander(expanderName)
for _, etc := range ExpanderTestCases {
template := etc.GetTemplate(t)
actualOutput, err := backend.ExpandTemplate(template)
if err != nil {
message := err.Error()
if !strings.Contains(message, etc.ExpectedError) {
t.Fatalf("error in test case '%s': %s\n", etc.Description, message)
}
} else {
if etc.ExpectedError != "" {
t.Fatalf("expected error did not occur in test case '%s': %s\n",
etc.Description, etc.ExpectedError)
}
expandAndVerifyOutput(t, actualOutput, etc.Description)
}
}
func
TestPropertiesPython
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
Properties
:
map
[
string
]
interface
{}{
"prop1"
:
3.0
,
"prop2"
:
"foo"
,
},
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
pyExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.py"
,
Content
:
content
([]
string
{
"def GenerateConfig(ctx):"
,
" return '''resources:"
,
"- name: foo"
,
" type: %(prop2)s"
,
" properties:"
,
" something: %(prop1)s"
,
"''' % ctx.properties"
,
}),
},
},
},
},
&
common
.
ExpansionResponse
{
Resources
:
[]
interface
{}{
map
[
string
]
interface
{}{
"name"
:
"foo"
,
"properties"
:
map
[
string
]
interface
{}{
"something"
:
3.0
,
},
"type"
:
"foo"
,
},
},
},
""
,
// Error
)
}
func
TestMultiFileJinja
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"{% include 'templates/secondary.jinja' %}"
}),
},
{
Path
:
"templates/secondary.jinja"
,
Content
:
content
([]
string
{
"resources:"
,
"- name: foo"
,
" type: bar"
,
}),
},
},
},
},
&
common
.
ExpansionResponse
{
Resources
:
[]
interface
{}{
map
[
string
]
interface
{}{
"name"
:
"foo"
,
"type"
:
"bar"
,
},
},
},
""
,
// Error
)
}
var
schemaContent
=
content
([]
string
{
`{`
,
` "required": ["prop1", "prop2"],`
,
` "additionalProperties": false,`
,
` "properties": {`
,
` "prop1": {`
,
` "description": "Nice description.",`
,
` "type": "integer"`
,
` },`
,
` "prop2": {`
,
` "description": "Nice description.",`
,
` "type": "string"`
,
` }`
,
` }`
,
`}`
,
})
func
TestSchema
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
Properties
:
map
[
string
]
interface
{}{
"prop1"
:
3.0
,
"prop2"
:
"foo"
,
},
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
Schema
:
"Schema.yaml"
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"Schema.yaml"
,
Content
:
schemaContent
,
},
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"resources:"
,
"- name: foo"
,
" type: {{ properties.prop2 }}"
,
" properties:"
,
" something: {{ properties.prop1 }}"
,
}),
},
},
},
},
&
common
.
ExpansionResponse
{
Resources
:
[]
interface
{}{
map
[
string
]
interface
{}{
"name"
:
"foo"
,
"properties"
:
map
[
string
]
interface
{}{
"something"
:
3.0
,
},
"type"
:
"foo"
,
},
},
},
""
,
// Error
)
}
func
TestSchemaFail
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
Properties
:
map
[
string
]
interface
{}{
"prop1"
:
3.0
,
"prop3"
:
"foo"
,
},
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
Schema
:
"Schema.yaml"
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"Schema.yaml"
,
Content
:
schemaContent
,
},
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"resources:"
,
"- name: foo"
,
" type: {{ properties.prop2 }}"
,
" properties:"
,
" something: {{ properties.prop1 }}"
,
}),
},
},
},
},
nil
,
// Response.
"Invalid properties for"
,
)
}
func
TestMultiFileJinjaMissing
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"{% include 'templates/secondary.jinja' %}"
}),
},
},
},
},
nil
,
// Response
"TemplateNotFound: templates/secondary.jinja"
,
)
}
func
TestMultiFilePython
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
pyExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.py"
,
Content
:
content
([]
string
{
"from templates import second"
,
"import templates.third"
,
"def GenerateConfig(ctx):"
,
" t2 = second.Gen()"
,
" t3 = templates.third.Gen()"
,
" return t2"
,
}),
},
{
Path
:
"templates/second.py"
,
Content
:
content
([]
string
{
"def Gen():"
,
" return '''resources:"
,
"- name: foo"
,
" type: bar"
,
"'''"
,
}),
},
{
Path
:
"templates/third.py"
,
Content
:
content
([]
string
{
"def Gen():"
,
" return '''resources:"
,
"- name: foo"
,
" type: bar"
,
"'''"
,
}),
},
},
},
},
&
common
.
ExpansionResponse
{
Resources
:
[]
interface
{}{
map
[
string
]
interface
{}{
"name"
:
"foo"
,
"type"
:
"bar"
,
},
},
},
""
,
// Error
)
}
func
TestMultiFilePythonMissing
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
pyExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.py"
,
Content
:
content
([]
string
{
"from templates import second"
,
}),
},
},
},
},
nil
,
// Response
"cannot import name second"
,
// Error
)
}
func
TestWrongChartName
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
"WrongName"
,
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"resources:"
}),
},
},
},
},
nil
,
// Response
"Request chart invocation does not match provided chart"
,
)
}
func
TestEntrypointNotFound
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{},
},
},
nil
,
// Response
"The entrypoint in the chart.yaml cannot be found"
,
)
}
func
TestMalformedResource
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"resources:"
,
"fail"
,
}),
},
},
},
},
nil
,
// Response
"could not found expected ':'"
,
// [sic]
)
}
func
TestResourceNoName
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"resources:"
,
"- type: bar"
,
}),
},
},
},
},
nil
,
// Response.
"Resource does not have a name"
,
)
}
func
TestResourceNoType
(
t
*
testing
.
T
)
{
testExpansion
(
t
,
&
common
.
ExpansionRequest
{
ChartInvocation
:
&
common
.
Resource
{
Name
:
"test_invocation"
,
Type
:
funcName
(),
},
Chart
:
&
chart
.
Content
{
Chartfile
:
&
chart
.
Chartfile
{
Name
:
funcName
(),
Expander
:
jinjaExpander
,
},
Members
:
[]
*
chart
.
Member
{
{
Path
:
"templates/main.jinja"
,
Content
:
content
([]
string
{
"resources:"
,
"- name: foo"
,
}),
},
},
},
},
nil
,
// Response.
"Resource does not have type defined"
,
)
}
*/
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment