Skip to content

Commit 0a65a84

Browse files
committed
feat: use pure ISO 3166-1 alpha-2 and deprecate lowercase
See publiccodeyml/publiccode.yml#227
1 parent a17ccbd commit 0a65a84

28 files changed

+895
-166
lines changed

fields.go

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import (
77

88
"github.com/alranel/go-vcsurl/v2"
99
urlutil "github.com/italia/publiccode-parser-go/v4/internal"
10+
publiccodeValidator "github.com/italia/publiccode-parser-go/v4/validators"
1011
)
1112

1213
type validateFn func(publiccode PublicCode, parser Parser, network bool) error
1314

1415
// validateFields validates publiccode with additional rules not validatable
1516
// with a simple YAML schema.
1617
// It returns any error encountered as ValidationResults.
17-
func validateFieldsV0(publiccode PublicCode, parser Parser, network bool) error {
18+
func validateFieldsV0(publiccode PublicCode, parser Parser, network bool) error { //nolint:maintidx
1819
publiccodev0 := publiccode.(PublicCodeV0)
1920

2021
var vr ValidationResults
@@ -65,6 +66,37 @@ func validateFieldsV0(publiccode PublicCode, parser Parser, network bool) error
6566
}
6667
}
6768

69+
if publiccodev0.IntendedAudience != nil {
70+
// This is not ideal, but we need to revalidate the countries
71+
// here, because otherwise we could get a warning and the advice
72+
// to use uppercase on an invalid country.
73+
validate := publiccodeValidator.New()
74+
75+
if publiccodev0.IntendedAudience.Countries != nil {
76+
for i, c := range *publiccodev0.IntendedAudience.Countries {
77+
if validate.Var(c, "iso3166_1_alpha2_lower_or_upper") == nil && c == strings.ToLower(c) {
78+
vr = append(vr, ValidationWarning{
79+
fmt.Sprintf("intendedAudience.countries[%d]", i),
80+
fmt.Sprintf("Lowercase country codes are DEPRECATED. Use uppercase instead ('%s')", strings.ToUpper(c)),
81+
0, 0,
82+
})
83+
}
84+
}
85+
}
86+
87+
if publiccodev0.IntendedAudience.UnsupportedCountries != nil {
88+
for i, c := range *publiccodev0.IntendedAudience.UnsupportedCountries {
89+
if validate.Var(c, "iso3166_1_alpha2_lower_or_upper") == nil && c == strings.ToLower(c) {
90+
vr = append(vr, ValidationWarning{
91+
fmt.Sprintf("intendedAudience.unsupportedCountries[%d]", i),
92+
fmt.Sprintf("Lowercase country codes are DEPRECATED. Use uppercase instead ('%s')", strings.ToUpper(c)),
93+
0, 0,
94+
})
95+
}
96+
}
97+
}
98+
}
99+
68100
if publiccodev0.Legal.AuthorsFile != nil {
69101
vr = append(vr, ValidationWarning{"legal.authorsFile", "This key is DEPRECATED and will be removed in the future. It's safe to drop it", 0, 0})
70102

@@ -138,13 +170,25 @@ func validateFieldsV0(publiccode PublicCode, parser Parser, network bool) error
138170
}
139171
}
140172

141-
if publiccodev0.It != nil && publiccodev0.It.Conforme != nil {
173+
if (publiccodev0.It != nil && publiccodev0.It.Conforme != nil) ||
174+
(publiccodev0.IT != nil && publiccodev0.IT.Conforme != nil) {
142175
vr = append(vr, ValidationWarning{
143-
"it.conforme",
176+
"IT.conforme",
144177
"This key is DEPRECATED and will be removed in the future. It's safe to drop it", 0, 0,
145178
})
146179
}
147180

181+
if publiccodev0.It != nil {
182+
vr = append(vr, ValidationWarning{
183+
"it",
184+
"Lowercase country codes are DEPRECATED and will be removed in the future. Use 'IT' instead", 0, 0,
185+
})
186+
}
187+
188+
if publiccodev0.IT != nil && publiccodev0.It != nil {
189+
vr = append(vr, newValidationError("it", "'IT' key already present. Remove this key"))
190+
}
191+
148192
if len(vr) == 0 {
149193
return nil
150194
}

parser.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func NewDefaultParser() (*Parser, error) {
9191
}
9292

9393
// ParseStream reads the data and tries to parse it. Returns an error if fails.
94-
func (p *Parser) ParseStream(in io.Reader) (PublicCode, error) {
94+
func (p *Parser) ParseStream(in io.Reader) (PublicCode, error) { //nolint:maintidx
9595
b, err := io.ReadAll(in)
9696
if err != nil {
9797
return nil, ValidationResults{newValidationError("", fmt.Sprintf("Can't read the stream: %v", err))}
@@ -290,6 +290,12 @@ func (p *Parser) ParseStream(in io.Reader) (PublicCode, error) {
290290
}
291291
}
292292

293+
// If the deprecated 'it' was used instead of 'IT', copy the data
294+
// in the canonical 'IT' field.
295+
if v0, ok := publiccode.(PublicCodeV0); ok {
296+
v0.IT = v0.It
297+
}
298+
293299
if len(ve) == 0 {
294300
return publiccode, nil
295301
}

parser_test.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -409,14 +409,16 @@ func TestInvalidTestcasesV0(t *testing.T) {
409409
ValidationError{"intendedAudience", "wrong type for this field", 18, 1},
410410
},
411411
"intendedAudience_countries_invalid_country.yml": ValidationResults{
412-
ValidationError{"intendedAudience.countries[1]", "countries[1] must be a valid lowercase ISO 3166-1 alpha-2 two-letter country code", 18, 5},
413-
ValidationError{"intendedAudience.countries[2]", "countries[2] must be a valid lowercase ISO 3166-1 alpha-2 two-letter country code", 18, 5},
412+
ValidationError{"intendedAudience.countries[2]", "countries[2] must be a valid ISO 3166-1 alpha-2 two-letter country code", 18, 3},
413+
},
414+
"intendedAudience_countries_invalid_iso_3166_1_alpha_2.yml": ValidationResults{
415+
ValidationError{"intendedAudience.countries[2]", "countries[2] must be a valid ISO 3166-1 alpha-2 two-letter country code", 18, 3},
414416
},
415417
"intendedAudience_countries_wrong_type.yml": ValidationResults{
416418
ValidationError{"intendedAudience.countries", "wrong type for this field", 19, 1},
417419
},
418420
"intendedAudience_unsupportedCountries_invalid_country.yml": ValidationResults{
419-
ValidationError{"intendedAudience.unsupportedCountries[0]", "unsupportedCountries[0] must be a valid lowercase ISO 3166-1 alpha-2 two-letter country code", 18, 5},
421+
ValidationError{"intendedAudience.unsupportedCountries[0]", "unsupportedCountries[0] must be a valid ISO 3166-1 alpha-2 two-letter country code", 18, 3},
420422
},
421423
"intendedAudience_unsupportedCountries_wrong_type.yml": ValidationResults{
422424
ValidationError{"intendedAudience.unsupportedCountries", "wrong type for this field", 19, 1},
@@ -616,10 +618,17 @@ func TestInvalidTestcasesV0(t *testing.T) {
616618

617619
// it.*
618620
"it_countryExtensionVersion_invalid.yml": ValidationResults{
619-
ValidationError{"it.countryExtensionVersion", "countryExtensionVersion must be one of the following: \"0.2\" or \"1.0\"", 12, 3},
621+
ValidationError{"IT.countryExtensionVersion", "countryExtensionVersion must be one of the following: \"0.2\" or \"1.0\"", 12, 3},
620622
},
621623
"it_riuso_codiceIPA_invalid.yml": ValidationResults{
622-
ValidationError{"it.riuso.codiceIPA", "codiceIPA must be a valid Italian Public Administration Code (iPA) (see https://www.indicepa.gov.it/public-services/opendata-read-service.php?dstype=FS&filename=amministrazioni.txt)", 55, 5},
624+
ValidationError{"IT.riuso.codiceIPA", "codiceIPA must be a valid Italian Public Administration Code (iPA) (see https://www.indicepa.gov.it/public-services/opendata-read-service.php?dstype=FS&filename=amministrazioni.txt)", 55, 5},
625+
},
626+
"it_IT_duplicated.yml": ValidationResults{
627+
ValidationWarning{"it", "Lowercase country codes are DEPRECATED and will be removed in the future. Use 'IT' instead", 120, 1},
628+
ValidationError{"it", "'IT' key already present. Remove this key", 120, 1},
629+
},
630+
"it_wrong_case.yml": ValidationResults{
631+
ValidationError{"It", "field It not found in type publiccode.PublicCodeV0", 108, 1},
623632
},
624633

625634
// misc
@@ -685,7 +694,15 @@ func TestValidWithWarningsTestcasesV0(t *testing.T) {
685694
ValidationWarning{"outputTypes", "This key is DEPRECATED and will be removed in the future. It's safe to drop it", 50, 1},
686695
},
687696
"valid_with_it_conforme.yml": ValidationResults{
688-
ValidationWarning{"it.conforme", "This key is DEPRECATED and will be removed in the future. It's safe to drop it", 120, 3},
697+
ValidationWarning{"IT.conforme", "This key is DEPRECATED and will be removed in the future. It's safe to drop it", 120, 3},
698+
},
699+
"valid_with_country_specific_section_downcase.yml": ValidationResults{
700+
ValidationWarning{"it", "Lowercase country codes are DEPRECATED and will be removed in the future. Use 'IT' instead", 108, 1},
701+
},
702+
"valid_with_lowercase_countries.yml": ValidationResults{
703+
ValidationWarning{"intendedAudience.countries[0]", "Lowercase country codes are DEPRECATED. Use uppercase instead ('IT')", 30, 3},
704+
ValidationWarning{"intendedAudience.countries[1]", "Lowercase country codes are DEPRECATED. Use uppercase instead ('DE')", 30, 3},
705+
ValidationWarning{"intendedAudience.unsupportedCountries[0]", "Lowercase country codes are DEPRECATED. Use uppercase instead ('US')", 30, 3},
689706
},
690707
}
691708

@@ -717,6 +734,7 @@ func TestDecodeValueErrorsRemote(t *testing.T) {
717734
},
718735
ValidationWarning{"legal.authorsFile", "This key is DEPRECATED and will be removed in the future. It's safe to drop it", 48, 3},
719736
ValidationWarning{"description.it.genericName", "This key is DEPRECATED and will be removed in the future. It's safe to drop it", 12, 5},
737+
ValidationWarning{"it", "Lowercase country codes are DEPRECATED and will be removed in the future. Use 'IT' instead", 37, 1},
720738
}},
721739
}
722740

testdata/v0/invalid/intendedAudience_countries_invalid_country.yml

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,33 @@ developmentStatus: development
1515
softwareType: "standalone/other"
1616

1717
intendedAudience:
18-
countries:
19-
- it
20-
# Should NOT validate: this is not a valid country
21-
- mouseton
22-
# Should NOT validate: not lowercase ISO 3166-1 alpha2
23-
- DE
18+
countries:
19+
- IT
20+
- DE
21+
# Should NOT validate: this is not a valid country
22+
- mouseton
2423

2524
description:
2625
en:
2726
localisedName: Medusa
2827
shortDescription: >
29-
A rather short description which
30-
is probably useless
28+
A rather short description which
29+
is probably useless
3130
longDescription: >
32-
Very long description of this software, also split
33-
on multiple rows. You should note what the software
34-
is and why one should need it. This is 158 characters.
35-
Very long description of this software, also split
36-
on multiple rows. You should note what the software
37-
is and why one should need it. This is 316 characters.
38-
Very long description of this software, also split
39-
on multiple rows. You should note what the software
40-
is and why one should need it. This is 474 characters.
41-
Very long description of this software, also split
42-
on multiple rows. You should note what the software
43-
is and why one should need it. This is 632 characters.
31+
Very long description of this software, also split
32+
on multiple rows. You should note what the software
33+
is and why one should need it. This is 158 characters.
34+
Very long description of this software, also split
35+
on multiple rows. You should note what the software
36+
is and why one should need it. This is 316 characters.
37+
Very long description of this software, also split
38+
on multiple rows. You should note what the software
39+
is and why one should need it. This is 474 characters.
40+
Very long description of this software, also split
41+
on multiple rows. You should note what the software
42+
is and why one should need it. This is 632 characters.
4443
features:
45-
- Just one feature
44+
- Just one feature
4645

4746
legal:
4847
license: AGPL-3.0-or-later
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
publiccodeYmlVersion: "0.4"
2+
3+
name: Medusa
4+
url: "https://github.com/italia/developers.italia.it.git"
5+
releaseDate: "2017-04-15"
6+
7+
platforms:
8+
- web
9+
10+
categories:
11+
- cloud-management
12+
13+
developmentStatus: development
14+
15+
softwareType: "standalone/other"
16+
17+
intendedAudience:
18+
countries:
19+
- IT
20+
- DE
21+
# Should NOT validate: mixed case
22+
- Fr
23+
24+
description:
25+
en:
26+
localisedName: Medusa
27+
shortDescription: >
28+
A rather short description which
29+
is probably useless
30+
longDescription: >
31+
Very long description of this software, also split
32+
on multiple rows. You should note what the software
33+
is and why one should need it. This is 158 characters.
34+
Very long description of this software, also split
35+
on multiple rows. You should note what the software
36+
is and why one should need it. This is 316 characters.
37+
Very long description of this software, also split
38+
on multiple rows. You should note what the software
39+
is and why one should need it. This is 474 characters.
40+
Very long description of this software, also split
41+
on multiple rows. You should note what the software
42+
is and why one should need it. This is 632 characters.
43+
features:
44+
- Just one feature
45+
46+
legal:
47+
license: AGPL-3.0-or-later
48+
49+
maintenance:
50+
type: "community"
51+
52+
contacts:
53+
- name: Francesco Rossi
54+
55+
localisation:
56+
localisationReady: true
57+
availableLanguages:
58+
- en

testdata/v0/invalid/intendedAudience_scope_wrong_type.yml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,30 @@ developmentStatus: development
1515
softwareType: "standalone/other"
1616

1717
intendedAudience:
18-
# Should NOT validate: scope must be an array
19-
scope: {}
18+
# Should NOT validate: scope must be an array
19+
scope: {}
2020

2121
description:
2222
en:
2323
localisedName: Medusa
2424
shortDescription: >
25-
A rather short description which
26-
is probably useless
25+
A rather short description which
26+
is probably useless
2727
longDescription: >
28-
Very long description of this software, also split
29-
on multiple rows. You should note what the software
30-
is and why one should need it. This is 158 characters.
31-
Very long description of this software, also split
32-
on multiple rows. You should note what the software
33-
is and why one should need it. This is 316 characters.
34-
Very long description of this software, also split
35-
on multiple rows. You should note what the software
36-
is and why one should need it. This is 474 characters.
37-
Very long description of this software, also split
38-
on multiple rows. You should note what the software
39-
is and why one should need it. This is 632 characters.
28+
Very long description of this software, also split
29+
on multiple rows. You should note what the software
30+
is and why one should need it. This is 158 characters.
31+
Very long description of this software, also split
32+
on multiple rows. You should note what the software
33+
is and why one should need it. This is 316 characters.
34+
Very long description of this software, also split
35+
on multiple rows. You should note what the software
36+
is and why one should need it. This is 474 characters.
37+
Very long description of this software, also split
38+
on multiple rows. You should note what the software
39+
is and why one should need it. This is 632 characters.
4040
features:
41-
- Just one feature
41+
- Just one feature
4242

4343
legal:
4444
license: AGPL-3.0-or-later

testdata/v0/invalid/intendedAudience_unsupportedCountries_invalid_country.yml

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,31 @@ developmentStatus: development
1515
softwareType: "standalone/other"
1616

1717
intendedAudience:
18-
unsupportedCountries:
19-
# Should NOT validate: this is not a valid country
20-
- Mouseton
18+
unsupportedCountries:
19+
# Should NOT validate: this is not a valid country
20+
- Mouseton
2121

2222
description:
2323
en:
2424
localisedName: Medusa
2525
shortDescription: >
26-
A rather short description which
27-
is probably useless
26+
A rather short description which
27+
is probably useless
2828
longDescription: >
29-
Very long description of this software, also split
30-
on multiple rows. You should note what the software
31-
is and why one should need it. This is 158 characters.
32-
Very long description of this software, also split
33-
on multiple rows. You should note what the software
34-
is and why one should need it. This is 316 characters.
35-
Very long description of this software, also split
36-
on multiple rows. You should note what the software
37-
is and why one should need it. This is 474 characters.
38-
Very long description of this software, also split
39-
on multiple rows. You should note what the software
40-
is and why one should need it. This is 632 characters.
29+
Very long description of this software, also split
30+
on multiple rows. You should note what the software
31+
is and why one should need it. This is 158 characters.
32+
Very long description of this software, also split
33+
on multiple rows. You should note what the software
34+
is and why one should need it. This is 316 characters.
35+
Very long description of this software, also split
36+
on multiple rows. You should note what the software
37+
is and why one should need it. This is 474 characters.
38+
Very long description of this software, also split
39+
on multiple rows. You should note what the software
40+
is and why one should need it. This is 632 characters.
4141
features:
42-
- Just one feature
42+
- Just one feature
4343

4444
legal:
4545
license: AGPL-3.0-or-later

0 commit comments

Comments
 (0)