github.com/aavshr/aws-sdk-go@v1.41.3/private/model/api/load.go (about) 1 //go:build codegen 2 // +build codegen 3 4 package api 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "log" 10 "os" 11 "path/filepath" 12 "sort" 13 "strings" 14 ) 15 16 // APIs provides a set of API models loaded by API package name. 17 type APIs map[string]*API 18 19 // Loader provides the loading of APIs from files. 20 type Loader struct { 21 // The base Go import path the loaded models will be appended to. 22 BaseImport string 23 24 // Allows ignoring API models that are unsupported by the SDK without 25 // failing the load of other supported APIs. 26 IgnoreUnsupportedAPIs bool 27 } 28 29 // Load loads the API model files from disk returning the map of API package. 30 // Returns error if multiple API model resolve to the same package name. 31 func (l Loader) Load(modelPaths []string) (APIs, error) { 32 apis := APIs{} 33 for _, modelPath := range modelPaths { 34 a, err := loadAPI(modelPath, l.BaseImport, func(a *API) { 35 a.IgnoreUnsupportedAPIs = l.IgnoreUnsupportedAPIs 36 }) 37 if err != nil { 38 return nil, fmt.Errorf("failed to load API, %v, %v", modelPath, err) 39 } 40 41 if len(a.Operations) == 0 { 42 if l.IgnoreUnsupportedAPIs { 43 fmt.Fprintf(os.Stderr, "API has no operations, ignoring model %s, %v\n", 44 modelPath, a.ImportPath()) 45 continue 46 } 47 } 48 49 importPath := a.ImportPath() 50 if _, ok := apis[importPath]; ok { 51 return nil, fmt.Errorf( 52 "package names must be unique attempted to load %v twice. Second model file: %v", 53 importPath, modelPath) 54 } 55 apis[importPath] = a 56 } 57 58 return apis, nil 59 } 60 61 // attempts to load a model from disk into the import specified. Additional API 62 // options are invoked before to the API's Setup being called. 63 func loadAPI(modelPath, baseImport string, opts ...func(*API)) (*API, error) { 64 a := &API{ 65 BaseImportPath: baseImport, 66 BaseCrosslinkURL: "https://docs.aws.amazon.com", 67 } 68 69 modelFile := filepath.Base(modelPath) 70 modelDir := filepath.Dir(modelPath) 71 err := attachModelFiles(modelDir, 72 modelLoader{modelFile, a.Attach, true}, 73 modelLoader{"docs-2.json", a.AttachDocs, false}, 74 modelLoader{"paginators-1.json", a.AttachPaginators, false}, 75 modelLoader{"waiters-2.json", a.AttachWaiters, false}, 76 modelLoader{"examples-1.json", a.AttachExamples, false}, 77 modelLoader{"smoke.json", a.AttachSmokeTests, false}, 78 ) 79 if err != nil { 80 return nil, err 81 } 82 83 for _, opt := range opts { 84 opt(a) 85 } 86 87 if err = a.Setup(); err != nil { 88 return nil, err 89 } 90 91 return a, nil 92 } 93 94 type modelLoader struct { 95 Filename string 96 Loader func(string) error 97 Required bool 98 } 99 100 func attachModelFiles(modelPath string, modelFiles ...modelLoader) error { 101 for _, m := range modelFiles { 102 filepath := filepath.Join(modelPath, m.Filename) 103 _, err := os.Stat(filepath) 104 if os.IsNotExist(err) && !m.Required { 105 continue 106 } else if err != nil { 107 return fmt.Errorf("failed to load model file %v, %v", m.Filename, err) 108 } 109 110 if err = m.Loader(filepath); err != nil { 111 return fmt.Errorf("model load failed, %s, %v", modelPath, err) 112 } 113 } 114 115 return nil 116 } 117 118 // ExpandModelGlobPath returns a slice of model paths expanded from the glob 119 // pattern passed in. Returns the path of the model file to be loaded. Includes 120 // all versions of a service model. 121 // 122 // e.g: 123 // models/apis/*/*/api-2.json 124 // 125 // Or with specific model file: 126 // models/apis/service/version/api-2.json 127 func ExpandModelGlobPath(globs ...string) ([]string, error) { 128 modelPaths := []string{} 129 130 for _, g := range globs { 131 filepaths, err := filepath.Glob(g) 132 if err != nil { 133 return nil, err 134 } 135 for _, p := range filepaths { 136 modelPaths = append(modelPaths, p) 137 } 138 } 139 140 return modelPaths, nil 141 } 142 143 // TrimModelServiceVersions sorts the model paths by service version then 144 // returns recent model versions, and model version excluded. 145 // 146 // Uses the third from last path element to determine unique service. Only one 147 // service version will be included. 148 // 149 // models/apis/service/version/api-2.json 150 func TrimModelServiceVersions(modelPaths []string) (include, exclude []string) { 151 sort.Strings(modelPaths) 152 153 // Remove old API versions from list 154 m := map[string]struct{}{} 155 for i := len(modelPaths) - 1; i >= 0; i-- { 156 // service name is 2nd-to-last component 157 parts := strings.Split(modelPaths[i], string(filepath.Separator)) 158 svc := parts[len(parts)-3] 159 160 if _, ok := m[svc]; ok { 161 // Removed unused service version 162 exclude = append(exclude, modelPaths[i]) 163 continue 164 } 165 include = append(include, modelPaths[i]) 166 m[svc] = struct{}{} 167 } 168 169 return include, exclude 170 } 171 172 // Attach opens a file by name, and unmarshal its JSON data. 173 // Will proceed to setup the API if not already done so. 174 func (a *API) Attach(filename string) error { 175 a.path = filepath.Dir(filename) 176 f, err := os.Open(filename) 177 if err != nil { 178 return err 179 } 180 defer f.Close() 181 182 if err := json.NewDecoder(f).Decode(a); err != nil { 183 return fmt.Errorf("failed to decode %s, err: %v", filename, err) 184 } 185 186 return nil 187 } 188 189 // AttachString will unmarshal a raw JSON string, and setup the 190 // API if not already done so. 191 func (a *API) AttachString(str string) error { 192 json.Unmarshal([]byte(str), a) 193 194 if a.initialized { 195 return nil 196 } 197 198 return a.Setup() 199 } 200 201 // Setup initializes the API. 202 func (a *API) Setup() error { 203 if err := a.validateNoDocumentShapes(); err != nil { 204 return err 205 } 206 a.setServiceAliaseName() 207 a.setMetadataEndpointsKey() 208 a.writeShapeNames() 209 a.resolveReferences() 210 a.backfillErrorMembers() 211 212 if !a.NoRemoveUnusedShapes { 213 a.removeUnusedShapes() 214 } 215 216 a.fixStutterNames() 217 if err := a.validateShapeNames(); err != nil { 218 log.Fatalf(err.Error()) 219 } 220 a.renameExportable() 221 a.applyShapeNameAliases() 222 a.renameIOSuffixedShapeNames() 223 a.createInputOutputShapes() 224 a.writeInputOutputLocationName() 225 a.renameAPIPayloadShapes() 226 a.renameCollidingFields() 227 a.updateTopLevelShapeReferences() 228 if err := a.setupEventStreams(); err != nil { 229 return err 230 } 231 232 a.findEndpointDiscoveryOp() 233 a.injectUnboundedOutputStreaming() 234 235 // Enables generated types for APIs using REST-JSON and JSONRPC protocols. 236 // Other protocols will be added as supported. 237 a.enableGeneratedTypedErrors() 238 239 if err := a.customizationPasses(); err != nil { 240 return err 241 } 242 243 a.addHeaderMapDocumentation() 244 245 if !a.NoRemoveUnusedShapes { 246 a.removeUnusedShapes() 247 } 248 249 if !a.NoValidataShapeMethods { 250 a.addShapeValidations() 251 } 252 253 a.initialized = true 254 255 return nil 256 } 257 258 // UnsupportedAPIModelError provides wrapping of an error causing the API to 259 // fail to load because the SDK does not support the API service defined. 260 type UnsupportedAPIModelError struct { 261 Err error 262 } 263 264 func (e UnsupportedAPIModelError) Error() string { 265 return fmt.Sprintf("service API is not supported, %v", e.Err) 266 }