Skip to content

Commit 8971249

Browse files
authored
Changelog filter (Azure#19562)
* changelog filter * sort changelog * sort field * fix * fix * fix function not param * remove sort * fix LROFilter, need to determine the LRO changelog format * add changelogProcessor_test.go * fix testdata * lrofilter * fix * test directory name fix * fix * remove tmp value * enum filter * update enum test * add copyright header
1 parent a14c17b commit 8971249

File tree

12 files changed

+405
-13
lines changed

12 files changed

+405
-13
lines changed

eng/tools/generator/autorest/model/changelog.go

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,19 +124,48 @@ func getNewContents(c *delta.Content) []string {
124124
}
125125

126126
var items []string
127-
128-
if len(c.Consts) > 0 {
127+
if len(c.Consts) > 0 || len(c.TypeAliases) > 0 {
128+
newTypeAlias := make(map[string][]string)
129+
existedTypeAlias := make(map[string][]string)
129130
for _, k := range sortChangeItem(c.Consts) {
130-
line := fmt.Sprintf("New const `%s`", k)
131+
cs := c.Consts[k]
132+
if _, ok := c.TypeAliases[cs.Type]; ok {
133+
if alias, ok := newTypeAlias[cs.Type]; ok {
134+
alias = append(alias, k)
135+
newTypeAlias[cs.Type] = alias
136+
} else {
137+
alias = []string{k}
138+
newTypeAlias[cs.Type] = alias
139+
}
140+
} else {
141+
if alias, ok := existedTypeAlias[cs.Type]; ok {
142+
alias = append(alias, k)
143+
existedTypeAlias[cs.Type] = alias
144+
} else {
145+
existedTypeAlias[cs.Type] = []string{k}
146+
}
147+
}
148+
}
149+
150+
for _, k := range sortChangeItem(existedTypeAlias) {
151+
aliasValue := ""
152+
for _, cs := range existedTypeAlias[k] {
153+
aliasValue = fmt.Sprintf("%s`%s`, ", aliasValue, cs)
154+
}
155+
line := fmt.Sprintf("New value %s added to type alias `%s`", strings.TrimRight(strings.TrimSpace(aliasValue), ","), k)
131156
items = append(items, line)
132157
}
133-
}
134-
if len(c.TypeAliases) > 0 {
135-
for _, k := range sortChangeItem(c.TypeAliases) {
136-
line := fmt.Sprintf("New type alias `%s`", k)
158+
159+
for _, k := range sortChangeItem(newTypeAlias) {
160+
aliasValue := ""
161+
for _, cs := range newTypeAlias[k] {
162+
aliasValue = fmt.Sprintf("%s`%s`, ", aliasValue, cs)
163+
}
164+
line := fmt.Sprintf("New type alias `%s` with values %s", k, strings.TrimRight(strings.TrimSpace(aliasValue), ","))
137165
items = append(items, line)
138166
}
139167
}
168+
140169
if len(c.Funcs) > 0 {
141170
for _, k := range sortFuncItem(c.Funcs) {
142171
v := c.Funcs[k]
@@ -268,10 +297,23 @@ func getRemovedContent(removed *delta.Content) []string {
268297
}
269298
// write functions
270299
if len(removed.Funcs) > 0 {
300+
var lroItem []string
271301
for _, k := range sortFuncItem(removed.Funcs) {
302+
v := removed.Funcs[k]
303+
if v.ReplacedBy != nil {
304+
var line string
305+
if !strings.Contains(k, "Begin") {
306+
line = fmt.Sprintf("Operation `%s` has been changed to LRO, use `%s` instead.", k, *v.ReplacedBy)
307+
} else {
308+
line = fmt.Sprintf("Operation `%s` has been changed to non-LRO, use `%s` instead.", k, *v.ReplacedBy)
309+
}
310+
lroItem = append(lroItem, line)
311+
continue
312+
}
272313
line := fmt.Sprintf("Function `%s` has been removed", k)
273314
items = append(items, line)
274315
}
316+
items = append(items, lroItem...)
275317
}
276318
// write complete struct removal
277319
if len(removed.CompleteStructs) > 0 {
@@ -300,7 +342,7 @@ func getRemovedContent(removed *delta.Content) []string {
300342
}
301343

302344
type sortItem interface {
303-
delta.Signature | delta.StructDef | exports.Const | exports.TypeAlias | exports.Struct | string
345+
delta.Signature | delta.StructDef | exports.Const | exports.TypeAlias | exports.Struct | string | []string
304346
}
305347

306348
func sortChangeItem[T sortItem](change map[string]T) []string {

eng/tools/generator/cmd/v2/common/changlogProcessor.go renamed to eng/tools/generator/cmd/v2/common/changelogProcessor.go

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ package common
55

66
import (
77
"encoding/json"
8+
"fmt"
89
"io/ioutil"
910
"log"
1011
"net/http"
1112
"path"
13+
"regexp"
1214
"sort"
1315
"strings"
1416

1517
"github.com/Azure/azure-sdk-for-go/eng/tools/generator/autorest/model"
1618
"github.com/Azure/azure-sdk-for-go/eng/tools/generator/repo"
19+
"github.com/Azure/azure-sdk-for-go/eng/tools/internal/delta"
1720
"github.com/Azure/azure-sdk-for-go/eng/tools/internal/exports"
1821
"github.com/go-git/go-git/v5"
1922
"github.com/go-git/go-git/v5/config"
@@ -189,7 +192,7 @@ func GetExportsFromTag(sdkRepo repo.SDKRepository, packagePath, tag string) (*ex
189192
return &result, nil
190193
}
191194

192-
func FilterChangelog(changelog *model.Changelog) {
195+
func MarshalUnmarshalFilter(changelog *model.Changelog) {
193196
if changelog.Modified != nil {
194197
if changelog.Modified.AdditiveChanges != nil {
195198
removeMarshalUnmarshalFunc(changelog.Modified.AdditiveChanges.Funcs)
@@ -207,3 +210,144 @@ func removeMarshalUnmarshalFunc(funcs map[string]exports.Func) {
207210
}
208211
}
209212
}
213+
214+
func FilterChangelog(changelog *model.Changelog, opts ...func(changelog *model.Changelog)) {
215+
if changelog.Modified != nil {
216+
for _, opt := range opts {
217+
opt(changelog)
218+
}
219+
}
220+
}
221+
222+
func EnumFilter(changelog *model.Changelog) {
223+
if changelog.Modified.HasAdditiveChanges() {
224+
if changelog.Modified.AdditiveChanges != nil && changelog.Modified.AdditiveChanges.TypeAliases != nil {
225+
for typeAliases := range changelog.Modified.AdditiveChanges.TypeAliases {
226+
funcKeys, funcExist := searchKey(changelog.Modified.AdditiveChanges.Funcs, typeAliases, "Possible")
227+
if funcExist && len(funcKeys) == 1 {
228+
for _, f := range funcKeys {
229+
delete(changelog.Modified.AdditiveChanges.Funcs, f)
230+
}
231+
}
232+
}
233+
}
234+
}
235+
236+
if changelog.Modified.HasBreakingChanges() {
237+
enumOperation(changelog.Modified.BreakingChanges.Removed)
238+
}
239+
}
240+
241+
func enumOperation(content *delta.Content) {
242+
if content != nil && content.TypeAliases != nil {
243+
for typeAliases := range content.TypeAliases {
244+
constKeys, constExist := searchKey(content.Consts, typeAliases, "")
245+
funcKeys, funcExist := searchKey(content.Funcs, typeAliases, "Possible")
246+
247+
if constExist && funcExist && len(funcKeys) == 1 {
248+
for _, c := range constKeys {
249+
delete(content.Consts, c)
250+
}
251+
for _, f := range funcKeys {
252+
delete(content.Funcs, f)
253+
}
254+
}
255+
}
256+
}
257+
}
258+
259+
func searchKey[T exports.Const | exports.Func | exports.Struct](m map[string]T, key1, prefix string) ([]string, bool) {
260+
keys := make([]string, 0)
261+
for k := range m {
262+
if regexp.MustCompile(fmt.Sprintf("^%s%s\\w*", prefix, key1)).MatchString(k) {
263+
keys = append(keys, k)
264+
}
265+
}
266+
if len(keys) != 0 {
267+
return keys, true
268+
}
269+
return nil, false
270+
}
271+
272+
func FuncFilter(changelog *model.Changelog) {
273+
if changelog.Modified.HasAdditiveChanges() {
274+
funcOperation(changelog.Modified.AdditiveChanges)
275+
}
276+
277+
if changelog.Modified.HasBreakingChanges() {
278+
funcOperation(changelog.Modified.BreakingChanges.Removed)
279+
}
280+
}
281+
282+
func funcOperation(content *delta.Content) {
283+
if content != nil && content.Funcs != nil {
284+
for funcName, funcValue := range content.Funcs {
285+
clientFunc := strings.Split(funcName, ".")
286+
if len(clientFunc) == 2 {
287+
// the last parameter
288+
if funcValue.Params != nil {
289+
ps := strings.Split(*funcValue.Params, ",")
290+
clientFuncOptions := ps[len(ps)-1]
291+
clientFuncOptions = strings.TrimLeft(strings.TrimSpace(clientFuncOptions), "*")
292+
if clientFuncOptions != "" && content.CompleteStructs != nil {
293+
delete(content.Structs, clientFuncOptions)
294+
for i, v := range content.CompleteStructs {
295+
if v == clientFuncOptions {
296+
content.CompleteStructs = append(content.CompleteStructs[:i],
297+
content.CompleteStructs[i+1:]...)
298+
break
299+
}
300+
}
301+
}
302+
}
303+
304+
// the first return value
305+
if funcValue.Returns != nil {
306+
rs := strings.Split(*funcValue.Returns, ",")
307+
clientFuncResponse := rs[0]
308+
if strings.Contains(clientFunc[1], "runtime.Poller") {
309+
re := regexp.MustCompile("\\[(?P<response>.*)\\]")
310+
clientFuncResponse = re.FindString(clientFuncResponse)
311+
clientFuncResponse = re.ReplaceAllString(clientFuncResponse, "${response}")
312+
} else {
313+
clientFuncResponse = strings.TrimLeft(clientFuncResponse, "*")
314+
}
315+
if clientFuncResponse != "" && content.CompleteStructs != nil {
316+
delete(content.Structs, clientFuncResponse)
317+
for i, v := range content.CompleteStructs {
318+
if v == clientFuncResponse {
319+
content.CompleteStructs = append(content.CompleteStructs[:i],
320+
content.CompleteStructs[i+1:]...)
321+
break
322+
}
323+
}
324+
}
325+
}
326+
}
327+
}
328+
}
329+
}
330+
331+
// LROFilter LROFilter after OperationFilter
332+
func LROFilter(changelog *model.Changelog) {
333+
if changelog.Modified.HasBreakingChanges() && changelog.Modified.HasAdditiveChanges() && changelog.Modified.BreakingChanges.Removed != nil && changelog.Modified.BreakingChanges.Removed.Funcs != nil {
334+
removedContent := changelog.Modified.BreakingChanges.Removed
335+
for bFunc, v := range removedContent.Funcs {
336+
var beginFunc string
337+
clientFunc := strings.Split(bFunc, ".")
338+
if len(clientFunc) == 2 {
339+
if strings.Contains(clientFunc[1], "Begin") {
340+
clientFunc[1] = strings.ReplaceAll(clientFunc[1], "Being", "")
341+
beginFunc = fmt.Sprintf("%s.%s", clientFunc[0], clientFunc[1])
342+
} else {
343+
beginFunc = fmt.Sprintf("%s.Begin%s", clientFunc[0], clientFunc[1])
344+
}
345+
if _, ok := changelog.Modified.AdditiveChanges.Funcs[beginFunc]; ok {
346+
delete(changelog.Modified.AdditiveChanges.Funcs, beginFunc)
347+
v.ReplacedBy = &beginFunc
348+
removedContent.Funcs[bFunc] = v
349+
}
350+
}
351+
}
352+
}
353+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
package common_test
5+
6+
import (
7+
"fmt"
8+
"testing"
9+
10+
"github.com/Azure/azure-sdk-for-go/eng/tools/generator/autorest"
11+
"github.com/Azure/azure-sdk-for-go/eng/tools/generator/cmd/v2/common"
12+
"github.com/Azure/azure-sdk-for-go/eng/tools/internal/exports"
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestEnumFilter(t *testing.T) {
17+
oldExport, err := exports.Get("./testdata/old/enum")
18+
if err != nil {
19+
t.Fatal(err)
20+
}
21+
22+
newExport, err := exports.Get("./testdata/new/enum")
23+
if err != nil {
24+
t.Fatal(err)
25+
}
26+
27+
changelog, err := autorest.GetChangelogForPackage(&oldExport, &newExport)
28+
if err != nil {
29+
t.Fatal(err)
30+
}
31+
32+
common.FilterChangelog(changelog, common.EnumFilter)
33+
34+
excepted := fmt.Sprint("### Breaking Changes\n\n- Type alias `EnumRemove` has been removed\n\n### Features Added\n\n- New value `EnumExistB` added to type alias `EnumExist`\n- New type alias `EnumAdd` with values `EnumAddA`, `EnumAddB`\n")
35+
assert.Equal(t, excepted, changelog.ToCompactMarkdown())
36+
}
37+
38+
func TestFuncFilter(t *testing.T) {
39+
oldExport, err := exports.Get("./testdata/old/operation")
40+
if err != nil {
41+
t.Fatal(err)
42+
}
43+
44+
newExport, err := exports.Get("./testdata/new/operation")
45+
if err != nil {
46+
t.Fatal(err)
47+
}
48+
49+
changelog, err := autorest.GetChangelogForPackage(&oldExport, &newExport)
50+
if err != nil {
51+
t.Fatal(err)
52+
}
53+
54+
common.FilterChangelog(changelog, common.FuncFilter)
55+
56+
excepted := fmt.Sprint("### Breaking Changes\n\n- Function `*Client.Update` has been removed\n\n### Features Added\n\n- New function `*Client.BeginCreateOrUpdate(string, *ClientBeginCreateOrUpdateOptions) (ClientBeginCreateOrUpdateResponse, error)`\n")
57+
assert.Equal(t, excepted, changelog.ToCompactMarkdown())
58+
}
59+
60+
func TestLROFilter(t *testing.T) {
61+
oldExport, err := exports.Get("./testdata/old/lro")
62+
if err != nil {
63+
t.Fatal(err)
64+
}
65+
66+
newExport, err := exports.Get("./testdata/new/lro")
67+
if err != nil {
68+
t.Fatal(err)
69+
}
70+
71+
changelog, err := autorest.GetChangelogForPackage(&oldExport, &newExport)
72+
if err != nil {
73+
t.Fatal(err)
74+
}
75+
76+
common.FilterChangelog(changelog, common.FuncFilter, common.LROFilter)
77+
78+
excepted := fmt.Sprint("### Breaking Changes\n\n- Operation `*Client.CreateOrUpdate` has been changed to LRO, use `*Client.BeginCreateOrUpdate` instead.\n")
79+
assert.Equal(t, excepted, changelog.ToCompactMarkdown())
80+
}

eng/tools/generator/cmd/v2/common/generation.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@ func (ctx *GenerateContext) GenerateForSingleRPNamespace(generateParam *Generate
209209
if err != nil {
210210
return nil, err
211211
}
212-
FilterChangelog(changelog)
212+
213+
log.Printf("filter changelog...")
214+
FilterChangelog(changelog, MarshalUnmarshalFilter, EnumFilter, FuncFilter, LROFilter)
213215

214216
if onBoard {
215217
log.Printf("Replace {{NewClientName}} placeholder in the README.md ")
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
package enum
5+
6+
type EnumAdd string
7+
8+
const (
9+
EnumAddA EnumAdd = "A"
10+
EnumAddB EnumAdd = "B"
11+
)
12+
13+
func PossibleEnumAddValues() []EnumAdd {
14+
return []EnumAdd{
15+
EnumAddA,
16+
EnumAddB,
17+
}
18+
}
19+
20+
type EnumExist string
21+
22+
const (
23+
EnumExistA EnumExist = "A"
24+
EnumExistB EnumExist = "B"
25+
)
26+
27+
func PossibleEnumExistValues() []EnumExist {
28+
return []EnumExist{
29+
EnumExistA,
30+
EnumExistB,
31+
}
32+
}

0 commit comments

Comments
 (0)