diff --git a/.travis.yml b/.travis.yml index e87f138ef..040765e99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: jammy language: go go: - - 1.24.3 + - 1.25.13 services: - docker diff --git a/Dockerfile b/Dockerfile index 9a5fe5424..2cd21b521 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Development -FROM golang:1.24.3-alpine AS development +FROM golang:1.25.13-alpine AS development WORKDIR /go/src/github.com/tidepool-org/clinic RUN apk --no-cache update && \ apk --no-cache upgrade && \ @@ -13,7 +13,7 @@ RUN ./build.sh CMD ["air"] # Production -FROM golang:1.24.3-alpine AS production +FROM golang:1.25.13-alpine AS production WORKDIR /go/src/github.com/tidepool-org/clinic RUN apk --no-cache update && \ apk --no-cache upgrade && \ diff --git a/api/gen_server.go b/api/gen_server.go index 5e37acfc3..68a3501e3 100644 --- a/api/gen_server.go +++ b/api/gen_server.go @@ -1,6 +1,6 @@ // Package api provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.8.0 DO NOT EDIT. package api import ( @@ -13,235 +13,235 @@ import ( // ServerInterface represents all server handlers. type ServerInterface interface { - // List All Clinicians + // ListAllClinicians List All Clinicians // (GET /v1/clinicians) ListAllClinicians(ctx echo.Context, params ListAllCliniciansParams) error - // List Clinics for Clinician + // ListClinicsForClinician List Clinics for Clinician // (GET /v1/clinicians/{userId}/clinics) ListClinicsForClinician(ctx echo.Context, userId UserId, params ListClinicsForClinicianParams) error - // Enable Clinics + // EnableNewClinicExperience Enable Clinics // (POST /v1/clinicians/{userId}/migrate) EnableNewClinicExperience(ctx echo.Context, userId string) error - // List Clinics + // ListClinics List Clinics // (GET /v1/clinics) ListClinics(ctx echo.Context, params ListClinicsParams) error - // Create Clinic + // CreateClinic Create Clinic // (POST /v1/clinics) CreateClinic(ctx echo.Context) error - // Get Clinic by Share Code + // GetClinicByShareCode Get Clinic by Share Code // (GET /v1/clinics/share_code/{shareCode}) GetClinicByShareCode(ctx echo.Context, shareCode string) error - // Delete Clinic + // DeleteClinic Delete Clinic // (DELETE /v1/clinics/{clinicId}) DeleteClinic(ctx echo.Context, clinicId ClinicId) error - // Get Clinic + // GetClinic Get Clinic // (GET /v1/clinics/{clinicId}) GetClinic(ctx echo.Context, clinicId ClinicId) error - // Update Clinic + // UpdateClinic Update Clinic // (PUT /v1/clinics/{clinicId}) UpdateClinic(ctx echo.Context, clinicId ClinicId) error - // List Clinicians + // ListClinicians List Clinicians // (GET /v1/clinics/{clinicId}/clinicians) ListClinicians(ctx echo.Context, clinicId ClinicId, params ListCliniciansParams) error - // Create Clinician + // CreateClinician Create Clinician // (POST /v1/clinics/{clinicId}/clinicians) CreateClinician(ctx echo.Context, clinicId ClinicId) error - // Delete Clinician + // DeleteClinician Delete Clinician // (DELETE /v1/clinics/{clinicId}/clinicians/{clinicianId}) DeleteClinician(ctx echo.Context, clinicId ClinicId, clinicianId ClinicianId) error - // Get Clinician + // GetClinician Get Clinician // (GET /v1/clinics/{clinicId}/clinicians/{clinicianId}) GetClinician(ctx echo.Context, clinicId ClinicId, clinicianId ClinicianId) error - // Update Clinician + // UpdateClinician Update Clinician // (PUT /v1/clinics/{clinicId}/clinicians/{clinicianId}) UpdateClinician(ctx echo.Context, clinicId ClinicId, clinicianId ClinicianId) error - // Sync EHR Data + // SyncEHRData Sync EHR Data // (POST /v1/clinics/{clinicId}/ehr/sync) SyncEHRData(ctx echo.Context, clinicId ClinicId) error - // Delete Invited Clinician + // DeleteInvitedClinician Delete Invited Clinician // (DELETE /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician) DeleteInvitedClinician(ctx echo.Context, clinicId ClinicId, inviteId InviteId) error - // Get Invited Clinician + // GetInvitedClinician Get Invited Clinician // (GET /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician) GetInvitedClinician(ctx echo.Context, clinicId ClinicId, inviteId InviteId) error - // Associate Clinician to User + // AssociateClinicianToUser Associate Clinician to User // (PATCH /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician) AssociateClinicianToUser(ctx echo.Context, clinicId ClinicId, inviteId InviteId) error - // List Membership Restrictions + // ListMembershipRestrictions List Membership Restrictions // (GET /v1/clinics/{clinicId}/membership_restrictions) ListMembershipRestrictions(ctx echo.Context, clinicId ClinicId) error - // Update Membership Restrictions + // UpdateMembershipRestrictions Update Membership Restrictions // (PUT /v1/clinics/{clinicId}/membership_restrictions) UpdateMembershipRestrictions(ctx echo.Context, clinicId ClinicId) error - // Merge Clinic + // MergeClinic Merge Clinic // (POST /v1/clinics/{clinicId}/merge) MergeClinic(ctx echo.Context, clinicId ClinicId) error - // Trigger initial migration + // TriggerInitialMigration Trigger initial migration // (POST /v1/clinics/{clinicId}/migrate) TriggerInitialMigration(ctx echo.Context, clinicId string) error - // Retrieve Migration Status + // ListMigrations Retrieve Migration Status // (GET /v1/clinics/{clinicId}/migrations) ListMigrations(ctx echo.Context, clinicId string) error - // Migrate Legacy Clinician Patients + // MigrateLegacyClinicianPatients Migrate Legacy Clinician Patients // (POST /v1/clinics/{clinicId}/migrations) MigrateLegacyClinicianPatients(ctx echo.Context, clinicId string) error - // Get Migration + // GetMigration Get Migration // (GET /v1/clinics/{clinicId}/migrations/{userId}) GetMigration(ctx echo.Context, clinicId ClinicIdV1, userId UserId) error - // Update Migration + // UpdateMigration Update Migration // (PATCH /v1/clinics/{clinicId}/migrations/{userId}) UpdateMigration(ctx echo.Context, clinicId ClinicIdV1, userId UserId) error - // Get Patient Count + // GetPatientCount Get Patient Count // (GET /v1/clinics/{clinicId}/patient_count) GetPatientCount(ctx echo.Context, clinicId ClinicId) error - // Refresh Patient Count + // RefreshPatientCount Refresh Patient Count // (POST /v1/clinics/{clinicId}/patient_count/refresh) RefreshPatientCount(ctx echo.Context, clinicId ClinicId) error - // Create Patient Tag + // CreatePatientTag Create Patient Tag // (POST /v1/clinics/{clinicId}/patient_tags) CreatePatientTag(ctx echo.Context, clinicId ClinicId) error - // Delete Patient Tag + // DeletePatientTag Delete Patient Tag // (DELETE /v1/clinics/{clinicId}/patient_tags/{patientTagId}) DeletePatientTag(ctx echo.Context, clinicId ClinicId, patientTagId PatientTagId) error - // Update Patient Tag + // UpdatePatientTag Update Patient Tag // (PUT /v1/clinics/{clinicId}/patient_tags/{patientTagId}) UpdatePatientTag(ctx echo.Context, clinicId ClinicId, patientTagId PatientTagId) error - // Convert Patient Tag to Site + // ConvertPatientTagToSite Convert Patient Tag to Site // (POST /v1/clinics/{clinicId}/patient_tags/{patientTagId}/site) ConvertPatientTagToSite(ctx echo.Context, clinicId ClinicId, patientTagId PatientTagId) error - // List Patients + // ListPatients List Patients // (GET /v1/clinics/{clinicId}/patients) ListPatients(ctx echo.Context, clinicId ClinicId, params ListPatientsParams) error - // Create Patient Account + // CreatePatientAccount Create Patient Account // (POST /v1/clinics/{clinicId}/patients) CreatePatientAccount(ctx echo.Context, clinicId ClinicId) error - // Assign Patient Tag To Clinic Patients + // AssignPatientTagToClinicPatients Assign Patient Tag To Clinic Patients // (POST /v1/clinics/{clinicId}/patients/assign_tag/{patientTagId}) AssignPatientTagToClinicPatients(ctx echo.Context, clinicId ClinicId, patientTagId PatientTagId) error - // Delete Patient Tag From Clinic Patients + // DeletePatientTagFromClinicPatients Delete Patient Tag From Clinic Patients // (POST /v1/clinics/{clinicId}/patients/delete_tag/{patientTagId}) DeletePatientTagFromClinicPatients(ctx echo.Context, clinicId ClinicId, patientTagId PatientTagId) error - // Delete Patient + // DeletePatient Delete Patient // (DELETE /v1/clinics/{clinicId}/patients/{patientId}) DeletePatient(ctx echo.Context, clinicId ClinicId, patientId PatientId) error - // Get Patient + // GetPatient Get Patient // (GET /v1/clinics/{clinicId}/patients/{patientId}) GetPatient(ctx echo.Context, clinicId ClinicId, patientId PatientId) error - // Create Patient from Existing User + // CreatePatientFromUser Create Patient from Existing User // (POST /v1/clinics/{clinicId}/patients/{patientId}) CreatePatientFromUser(ctx echo.Context, clinicId ClinicId, patientId PatientId) error - // Update Patient + // UpdatePatient Update Patient // (PUT /v1/clinics/{clinicId}/patients/{patientId}) UpdatePatient(ctx echo.Context, clinicId ClinicId, patientId PatientId) error - // Connect Provider + // ConnectProvider Connect Provider // (POST /v1/clinics/{clinicId}/patients/{patientId}/connect/{providerId}) ConnectProvider(ctx echo.Context, clinicId ClinicId, patientId PatientId, providerId ProviderId) error - // Update Patient Permissions + // UpdatePatientPermissions Update Patient Permissions // (PUT /v1/clinics/{clinicId}/patients/{patientId}/permissions) UpdatePatientPermissions(ctx echo.Context, clinicId ClinicId, patientId PatientId) error - // Delete Patient Permission + // DeletePatientPermission Delete Patient Permission // (DELETE /v1/clinics/{clinicId}/patients/{patientId}/permissions/{permission}) DeletePatientPermission(ctx echo.Context, clinicId ClinicId, patientId PatientId, permission string) error - // Delete Patient Reviews + // DeletePatientReviews Delete Patient Reviews // (DELETE /v1/clinics/{clinicId}/patients/{patientId}/reviews) DeletePatientReviews(ctx echo.Context, clinicId ClinicId, patientId PatientId) error - // Update Patient Reviews + // UpdatePatientReviews Update Patient Reviews // (PUT /v1/clinics/{clinicId}/patients/{patientId}/reviews) UpdatePatientReviews(ctx echo.Context, clinicId ClinicId, patientId PatientId) error - // Send Upload Reminder + // SendUploadReminder Send Upload Reminder // (POST /v1/clinics/{clinicId}/patients/{patientId}/upload_reminder) SendUploadReminder(ctx echo.Context, clinicId ClinicId, patientId PatientId) error - // Generate Clinic Merge Report + // GenerateMergeReport Generate Clinic Merge Report // (POST /v1/clinics/{clinicId}/reports/merge) GenerateMergeReport(ctx echo.Context, clinicId ClinicId) error - // Add Service Account + // AddServiceAccount Add Service Account // (POST /v1/clinics/{clinicId}/service_accounts) AddServiceAccount(ctx echo.Context, clinicId ClinicId) error - // Get EHR Settings + // GetEHRSettings Get EHR Settings // (GET /v1/clinics/{clinicId}/settings/ehr) GetEHRSettings(ctx echo.Context, clinicId ClinicId) error - // Update EHR Settings + // UpdateEHRSettings Update EHR Settings // (PUT /v1/clinics/{clinicId}/settings/ehr) UpdateEHRSettings(ctx echo.Context, clinicId ClinicId) error - // Get MRN Settings + // GetMRNSettings Get MRN Settings // (GET /v1/clinics/{clinicId}/settings/mrn) GetMRNSettings(ctx echo.Context, clinicId ClinicId) error - // Update MRN Settings + // UpdateMRNSettings Update MRN Settings // (PUT /v1/clinics/{clinicId}/settings/mrn) UpdateMRNSettings(ctx echo.Context, clinicId ClinicId) error - // Get Patient Count Settings + // GetPatientCountSettings Get Patient Count Settings // (GET /v1/clinics/{clinicId}/settings/patient_count) GetPatientCountSettings(ctx echo.Context, clinicId ClinicId) error - // Update Patient Count Settings + // UpdatePatientCountSettings Update Patient Count Settings // (PUT /v1/clinics/{clinicId}/settings/patient_count) UpdatePatientCountSettings(ctx echo.Context, clinicId ClinicId) error - // Create a Site + // CreateSite Create a Site // (POST /v1/clinics/{clinicId}/sites) CreateSite(ctx echo.Context, clinicId ClinicId) error - // Delete a Site + // DeleteSite Delete a Site // (DELETE /v1/clinics/{clinicId}/sites/{siteId}) DeleteSite(ctx echo.Context, clinicId ClinicId, siteId SiteId) error - // Update a Site + // UpdateSite Update a Site // (PUT /v1/clinics/{clinicId}/sites/{siteId}) UpdateSite(ctx echo.Context, clinicId ClinicId, siteId SiteId) error - // Merge two sites + // MergeSite Merge two sites // (POST /v1/clinics/{clinicId}/sites/{siteId}/merge) MergeSite(ctx echo.Context, clinicId ClinicId, siteId SiteId) error - // Update Suppressed Notifications + // UpdateSuppressedNotifications Update Suppressed Notifications // (POST /v1/clinics/{clinicId}/suppressed_notifications) UpdateSuppressedNotifications(ctx echo.Context, clinicId ClinicId) error - // View TIDE Report + // TideReport View TIDE Report // (GET /v1/clinics/{clinicId}/tide_report) TideReport(ctx echo.Context, clinicId ClinicId, params TideReportParams) error - // Update Tier + // UpdateTier Update Tier // (POST /v1/clinics/{clinicId}/tier) UpdateTier(ctx echo.Context, clinicId ClinicId) error - // Find Patients + // FindPatients Find Patients // (GET /v1/patients) FindPatients(ctx echo.Context, params FindPatientsParams) error - // Sync EHR Data for Patient + // SyncEHRDataForPatient Sync EHR Data for Patient // (POST /v1/patients/{patientId}/ehr/sync) SyncEHRDataForPatient(ctx echo.Context, patientId PatientId) error - // UpdatePatientSummary + // UpdatePatientSummary UpdatePatientSummary // (POST /v1/patients/{patientId}/summary) UpdatePatientSummary(ctx echo.Context, patientId PatientId) error - // List Clinics for Patient + // ListClinicsForPatient List Clinics for Patient // (GET /v1/patients/{userId}/clinics) ListClinicsForPatient(ctx echo.Context, userId UserId, params ListClinicsForPatientParams) error - // Create or update a data source for a patient + // UpdatePatientDataSources Create or update a data source for a patient // (PUT /v1/patients/{userId}/data_sources) UpdatePatientDataSources(ctx echo.Context, userId UserId) error - // Redox EHR Endpoint + // ProcessEHRMessage Redox EHR Endpoint // (POST /v1/redox) ProcessEHRMessage(ctx echo.Context) error - // Match Clinic and Patient + // MatchClinicAndPatient Match Clinic and Patient // (POST /v1/redox/match) MatchClinicAndPatient(ctx echo.Context) error - // Redox Verify Endpoint + // VerifyEndpoint Redox Verify Endpoint // (POST /v1/redox/verify) VerifyEndpoint(ctx echo.Context) error - // DeletePatientSummary + // DeletePatientSummary DeletePatientSummary // (DELETE /v1/summaries/{summaryId}/clinics) DeletePatientSummary(ctx echo.Context, summaryId SummaryId) error - // Remove User from Clinics + // DeleteUserFromClinics Remove User from Clinics // (DELETE /v1/users/{userId}/clinics) DeleteUserFromClinics(ctx echo.Context, userId UserId) error - // Update User Details + // UpdateClinicUserDetails Update User Details // (POST /v1/users/{userId}/clinics) UpdateClinicUserDetails(ctx echo.Context, userId UserId) error - // Notification Webhook + // XealthNotification Notification Webhook // (POST /v1/xealth/notification) XealthNotification(ctx echo.Context) error - // Preorder Form Webhook + // XealthPreorder Preorder Form Webhook // (POST /v1/xealth/preorder) XealthPreorder(ctx echo.Context) error - // Get Program Url + // XealthGetProgramUrl Get Program Url // (PUT /v1/xealth/program) XealthGetProgramUrl(ctx echo.Context) error - // Get Programs + // XealthGetPrograms Get Programs // (PUT /v1/xealth/programs) XealthGetPrograms(ctx echo.Context) error - // View PDF Report + // ViewPDFReport View PDF Report // (GET /v1/xealth/report/web/viewer.html) ViewPDFReport(ctx echo.Context, params ViewPDFReportParams) error } @@ -255,34 +255,32 @@ type ServerInterfaceWrapper struct { func (w *ServerInterfaceWrapper) ListAllClinicians(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params ListAllCliniciansParams // ------------- Optional query parameter "offset" ------------- - err = runtime.BindQueryParameter("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset) + err = runtime.BindQueryParameterWithOptions("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter offset: %s", err)) } // ------------- Optional query parameter "limit" ------------- - err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + err = runtime.BindQueryParameterWithOptions("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } // ------------- Optional query parameter "createdTimeStart" ------------- - err = runtime.BindQueryParameter("form", true, false, "createdTimeStart", ctx.QueryParams(), ¶ms.CreatedTimeStart) + err = runtime.BindQueryParameterWithOptions("form", true, false, "createdTimeStart", ctx.QueryParams(), ¶ms.CreatedTimeStart, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter createdTimeStart: %s", err)) } // ------------- Optional query parameter "createdTimeEnd" ------------- - err = runtime.BindQueryParameter("form", true, false, "createdTimeEnd", ctx.QueryParams(), ¶ms.CreatedTimeEnd) + err = runtime.BindQueryParameterWithOptions("form", true, false, "createdTimeEnd", ctx.QueryParams(), ¶ms.CreatedTimeEnd, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter createdTimeEnd: %s", err)) } @@ -298,25 +296,23 @@ func (w *ServerInterfaceWrapper) ListClinicsForClinician(ctx echo.Context) error // ------------- Path parameter "userId" ------------- var userId UserId - err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params ListClinicsForClinicianParams // ------------- Optional query parameter "offset" ------------- - err = runtime.BindQueryParameter("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset) + err = runtime.BindQueryParameterWithOptions("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter offset: %s", err)) } // ------------- Optional query parameter "limit" ------------- - err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + err = runtime.BindQueryParameterWithOptions("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } @@ -332,13 +328,11 @@ func (w *ServerInterfaceWrapper) EnableNewClinicExperience(ctx echo.Context) err // ------------- Path parameter "userId" ------------- var userId string - err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.EnableNewClinicExperience(ctx, userId) return err @@ -348,48 +342,46 @@ func (w *ServerInterfaceWrapper) EnableNewClinicExperience(ctx echo.Context) err func (w *ServerInterfaceWrapper) ListClinics(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params ListClinicsParams // ------------- Optional query parameter "limit" ------------- - err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + err = runtime.BindQueryParameterWithOptions("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } // ------------- Optional query parameter "offset" ------------- - err = runtime.BindQueryParameter("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset) + err = runtime.BindQueryParameterWithOptions("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter offset: %s", err)) } // ------------- Optional query parameter "shareCode" ------------- - err = runtime.BindQueryParameter("form", true, false, "shareCode", ctx.QueryParams(), ¶ms.ShareCode) + err = runtime.BindQueryParameterWithOptions("form", true, false, "shareCode", ctx.QueryParams(), ¶ms.ShareCode, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter shareCode: %s", err)) } // ------------- Optional query parameter "createdTimeStart" ------------- - err = runtime.BindQueryParameter("form", true, false, "createdTimeStart", ctx.QueryParams(), ¶ms.CreatedTimeStart) + err = runtime.BindQueryParameterWithOptions("form", true, false, "createdTimeStart", ctx.QueryParams(), ¶ms.CreatedTimeStart, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter createdTimeStart: %s", err)) } // ------------- Optional query parameter "createdTimeEnd" ------------- - err = runtime.BindQueryParameter("form", true, false, "createdTimeEnd", ctx.QueryParams(), ¶ms.CreatedTimeEnd) + err = runtime.BindQueryParameterWithOptions("form", true, false, "createdTimeEnd", ctx.QueryParams(), ¶ms.CreatedTimeEnd, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter createdTimeEnd: %s", err)) } // ------------- Optional query parameter "ehrEnabled" ------------- - err = runtime.BindQueryParameter("form", true, false, "ehrEnabled", ctx.QueryParams(), ¶ms.EhrEnabled) + err = runtime.BindQueryParameterWithOptions("form", true, false, "ehrEnabled", ctx.QueryParams(), ¶ms.EhrEnabled, runtime.BindQueryParameterOptions{Type: "boolean", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter ehrEnabled: %s", err)) } @@ -403,8 +395,6 @@ func (w *ServerInterfaceWrapper) ListClinics(ctx echo.Context) error { func (w *ServerInterfaceWrapper) CreateClinic(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.CreateClinic(ctx) return err @@ -416,13 +406,11 @@ func (w *ServerInterfaceWrapper) GetClinicByShareCode(ctx echo.Context) error { // ------------- Path parameter "shareCode" ------------- var shareCode string - err = runtime.BindStyledParameterWithOptions("simple", "shareCode", ctx.Param("shareCode"), &shareCode, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "shareCode", ctx.Param("shareCode"), &shareCode, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter shareCode: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetClinicByShareCode(ctx, shareCode) return err @@ -434,13 +422,11 @@ func (w *ServerInterfaceWrapper) DeleteClinic(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeleteClinic(ctx, clinicId) return err @@ -452,13 +438,11 @@ func (w *ServerInterfaceWrapper) GetClinic(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetClinic(ctx, clinicId) return err @@ -470,13 +454,11 @@ func (w *ServerInterfaceWrapper) UpdateClinic(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateClinic(ctx, clinicId) return err @@ -488,46 +470,44 @@ func (w *ServerInterfaceWrapper) ListClinicians(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params ListCliniciansParams // ------------- Optional query parameter "search" ------------- - err = runtime.BindQueryParameter("form", true, false, "search", ctx.QueryParams(), ¶ms.Search) + err = runtime.BindQueryParameterWithOptions("form", true, false, "search", ctx.QueryParams(), ¶ms.Search, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter search: %s", err)) } // ------------- Optional query parameter "offset" ------------- - err = runtime.BindQueryParameter("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset) + err = runtime.BindQueryParameterWithOptions("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter offset: %s", err)) } // ------------- Optional query parameter "limit" ------------- - err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + err = runtime.BindQueryParameterWithOptions("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } // ------------- Optional query parameter "email" ------------- - err = runtime.BindQueryParameter("form", true, false, "email", ctx.QueryParams(), ¶ms.Email) + err = runtime.BindQueryParameterWithOptions("form", true, false, "email", ctx.QueryParams(), ¶ms.Email, runtime.BindQueryParameterOptions{Type: "string", Format: "email"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter email: %s", err)) } // ------------- Optional query parameter "role" ------------- - err = runtime.BindQueryParameter("form", true, false, "role", ctx.QueryParams(), ¶ms.Role) + err = runtime.BindQueryParameterWithOptions("form", true, false, "role", ctx.QueryParams(), ¶ms.Role, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter role: %s", err)) } @@ -543,13 +523,11 @@ func (w *ServerInterfaceWrapper) CreateClinician(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.CreateClinician(ctx, clinicId) return err @@ -561,7 +539,7 @@ func (w *ServerInterfaceWrapper) DeleteClinician(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -569,13 +547,11 @@ func (w *ServerInterfaceWrapper) DeleteClinician(ctx echo.Context) error { // ------------- Path parameter "clinicianId" ------------- var clinicianId ClinicianId - err = runtime.BindStyledParameterWithOptions("simple", "clinicianId", ctx.Param("clinicianId"), &clinicianId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicianId", ctx.Param("clinicianId"), &clinicianId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicianId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeleteClinician(ctx, clinicId, clinicianId) return err @@ -587,7 +563,7 @@ func (w *ServerInterfaceWrapper) GetClinician(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -595,13 +571,11 @@ func (w *ServerInterfaceWrapper) GetClinician(ctx echo.Context) error { // ------------- Path parameter "clinicianId" ------------- var clinicianId ClinicianId - err = runtime.BindStyledParameterWithOptions("simple", "clinicianId", ctx.Param("clinicianId"), &clinicianId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicianId", ctx.Param("clinicianId"), &clinicianId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicianId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetClinician(ctx, clinicId, clinicianId) return err @@ -613,7 +587,7 @@ func (w *ServerInterfaceWrapper) UpdateClinician(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -621,13 +595,11 @@ func (w *ServerInterfaceWrapper) UpdateClinician(ctx echo.Context) error { // ------------- Path parameter "clinicianId" ------------- var clinicianId ClinicianId - err = runtime.BindStyledParameterWithOptions("simple", "clinicianId", ctx.Param("clinicianId"), &clinicianId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicianId", ctx.Param("clinicianId"), &clinicianId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicianId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateClinician(ctx, clinicId, clinicianId) return err @@ -639,13 +611,11 @@ func (w *ServerInterfaceWrapper) SyncEHRData(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.SyncEHRData(ctx, clinicId) return err @@ -657,7 +627,7 @@ func (w *ServerInterfaceWrapper) DeleteInvitedClinician(ctx echo.Context) error // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -665,13 +635,11 @@ func (w *ServerInterfaceWrapper) DeleteInvitedClinician(ctx echo.Context) error // ------------- Path parameter "inviteId" ------------- var inviteId InviteId - err = runtime.BindStyledParameterWithOptions("simple", "inviteId", ctx.Param("inviteId"), &inviteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "inviteId", ctx.Param("inviteId"), &inviteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter inviteId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeleteInvitedClinician(ctx, clinicId, inviteId) return err @@ -683,7 +651,7 @@ func (w *ServerInterfaceWrapper) GetInvitedClinician(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -691,13 +659,11 @@ func (w *ServerInterfaceWrapper) GetInvitedClinician(ctx echo.Context) error { // ------------- Path parameter "inviteId" ------------- var inviteId InviteId - err = runtime.BindStyledParameterWithOptions("simple", "inviteId", ctx.Param("inviteId"), &inviteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "inviteId", ctx.Param("inviteId"), &inviteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter inviteId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetInvitedClinician(ctx, clinicId, inviteId) return err @@ -709,7 +675,7 @@ func (w *ServerInterfaceWrapper) AssociateClinicianToUser(ctx echo.Context) erro // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -717,13 +683,11 @@ func (w *ServerInterfaceWrapper) AssociateClinicianToUser(ctx echo.Context) erro // ------------- Path parameter "inviteId" ------------- var inviteId InviteId - err = runtime.BindStyledParameterWithOptions("simple", "inviteId", ctx.Param("inviteId"), &inviteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "inviteId", ctx.Param("inviteId"), &inviteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter inviteId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.AssociateClinicianToUser(ctx, clinicId, inviteId) return err @@ -735,13 +699,11 @@ func (w *ServerInterfaceWrapper) ListMembershipRestrictions(ctx echo.Context) er // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.ListMembershipRestrictions(ctx, clinicId) return err @@ -753,13 +715,11 @@ func (w *ServerInterfaceWrapper) UpdateMembershipRestrictions(ctx echo.Context) // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateMembershipRestrictions(ctx, clinicId) return err @@ -771,13 +731,11 @@ func (w *ServerInterfaceWrapper) MergeClinic(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.MergeClinic(ctx, clinicId) return err @@ -789,13 +747,11 @@ func (w *ServerInterfaceWrapper) TriggerInitialMigration(ctx echo.Context) error // ------------- Path parameter "clinicId" ------------- var clinicId string - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.TriggerInitialMigration(ctx, clinicId) return err @@ -807,13 +763,11 @@ func (w *ServerInterfaceWrapper) ListMigrations(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId string - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.ListMigrations(ctx, clinicId) return err @@ -825,13 +779,11 @@ func (w *ServerInterfaceWrapper) MigrateLegacyClinicianPatients(ctx echo.Context // ------------- Path parameter "clinicId" ------------- var clinicId string - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.MigrateLegacyClinicianPatients(ctx, clinicId) return err @@ -843,7 +795,7 @@ func (w *ServerInterfaceWrapper) GetMigration(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicIdV1 - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -851,13 +803,11 @@ func (w *ServerInterfaceWrapper) GetMigration(ctx echo.Context) error { // ------------- Path parameter "userId" ------------- var userId UserId - err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetMigration(ctx, clinicId, userId) return err @@ -869,7 +819,7 @@ func (w *ServerInterfaceWrapper) UpdateMigration(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicIdV1 - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -877,13 +827,11 @@ func (w *ServerInterfaceWrapper) UpdateMigration(ctx echo.Context) error { // ------------- Path parameter "userId" ------------- var userId UserId - err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateMigration(ctx, clinicId, userId) return err @@ -895,13 +843,11 @@ func (w *ServerInterfaceWrapper) GetPatientCount(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetPatientCount(ctx, clinicId) return err @@ -913,13 +859,11 @@ func (w *ServerInterfaceWrapper) RefreshPatientCount(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.RefreshPatientCount(ctx, clinicId) return err @@ -931,13 +875,11 @@ func (w *ServerInterfaceWrapper) CreatePatientTag(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.CreatePatientTag(ctx, clinicId) return err @@ -949,7 +891,7 @@ func (w *ServerInterfaceWrapper) DeletePatientTag(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -957,13 +899,11 @@ func (w *ServerInterfaceWrapper) DeletePatientTag(ctx echo.Context) error { // ------------- Path parameter "patientTagId" ------------- var patientTagId PatientTagId - err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientTagId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeletePatientTag(ctx, clinicId, patientTagId) return err @@ -975,7 +915,7 @@ func (w *ServerInterfaceWrapper) UpdatePatientTag(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -983,13 +923,11 @@ func (w *ServerInterfaceWrapper) UpdatePatientTag(ctx echo.Context) error { // ------------- Path parameter "patientTagId" ------------- var patientTagId PatientTagId - err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientTagId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdatePatientTag(ctx, clinicId, patientTagId) return err @@ -1001,7 +939,7 @@ func (w *ServerInterfaceWrapper) ConvertPatientTagToSite(ctx echo.Context) error // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -1009,13 +947,11 @@ func (w *ServerInterfaceWrapper) ConvertPatientTagToSite(ctx echo.Context) error // ------------- Path parameter "patientTagId" ------------- var patientTagId PatientTagId - err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientTagId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.ConvertPatientTagToSite(ctx, clinicId, patientTagId) return err @@ -1027,893 +963,891 @@ func (w *ServerInterfaceWrapper) ListPatients(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params ListPatientsParams // ------------- Optional query parameter "search" ------------- - err = runtime.BindQueryParameter("form", true, false, "search", ctx.QueryParams(), ¶ms.Search) + err = runtime.BindQueryParameterWithOptions("form", true, false, "search", ctx.QueryParams(), ¶ms.Search, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter search: %s", err)) } // ------------- Optional query parameter "offset" ------------- - err = runtime.BindQueryParameter("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset) + err = runtime.BindQueryParameterWithOptions("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter offset: %s", err)) } // ------------- Optional query parameter "limit" ------------- - err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + err = runtime.BindQueryParameterWithOptions("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } // ------------- Optional query parameter "sort" ------------- - err = runtime.BindQueryParameter("form", true, false, "sort", ctx.QueryParams(), ¶ms.Sort) + err = runtime.BindQueryParameterWithOptions("form", true, false, "sort", ctx.QueryParams(), ¶ms.Sort, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter sort: %s", err)) } // ------------- Optional query parameter "sortType" ------------- - err = runtime.BindQueryParameter("form", true, false, "sortType", ctx.QueryParams(), ¶ms.SortType) + err = runtime.BindQueryParameterWithOptions("form", true, false, "sortType", ctx.QueryParams(), ¶ms.SortType, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter sortType: %s", err)) } // ------------- Optional query parameter "period" ------------- - err = runtime.BindQueryParameter("form", true, false, "period", ctx.QueryParams(), ¶ms.Period) + err = runtime.BindQueryParameterWithOptions("form", true, false, "period", ctx.QueryParams(), ¶ms.Period, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter period: %s", err)) } // ------------- Optional query parameter "lastReviewed" ------------- - err = runtime.BindQueryParameter("form", true, false, "lastReviewed", ctx.QueryParams(), ¶ms.LastReviewed) + err = runtime.BindQueryParameterWithOptions("form", true, false, "lastReviewed", ctx.QueryParams(), ¶ms.LastReviewed, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter lastReviewed: %s", err)) } // ------------- Optional query parameter "cgm.max" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.max", ctx.QueryParams(), ¶ms.CgmMax) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.max", ctx.QueryParams(), ¶ms.CgmMax, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.max: %s", err)) } // ------------- Optional query parameter "cgm.min" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.min", ctx.QueryParams(), ¶ms.CgmMin) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.min", ctx.QueryParams(), ¶ms.CgmMin, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.min: %s", err)) } // ------------- Optional query parameter "cgm.averageGlucoseMmol" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.averageGlucoseMmol", ctx.QueryParams(), ¶ms.CgmAverageGlucoseMmol) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.averageGlucoseMmol", ctx.QueryParams(), ¶ms.CgmAverageGlucoseMmol, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.averageGlucoseMmol: %s", err)) } // ------------- Optional query parameter "cgm.glucoseManagementIndicator" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.glucoseManagementIndicator", ctx.QueryParams(), ¶ms.CgmGlucoseManagementIndicator) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.glucoseManagementIndicator", ctx.QueryParams(), ¶ms.CgmGlucoseManagementIndicator, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.glucoseManagementIndicator: %s", err)) } // ------------- Optional query parameter "cgm.timeCGMUsePercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeCGMUsePercent", ctx.QueryParams(), ¶ms.CgmTimeCGMUsePercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeCGMUsePercent", ctx.QueryParams(), ¶ms.CgmTimeCGMUsePercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeCGMUsePercent: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryLowPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryLowPercent", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryLowPercent", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryLowPercent: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyLowPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyLowPercent", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyLowPercent", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyLowPercent: %s", err)) } // ------------- Optional query parameter "cgm.timeInLowPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInLowPercent", ctx.QueryParams(), ¶ms.CgmTimeInLowPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInLowPercent", ctx.QueryParams(), ¶ms.CgmTimeInLowPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInLowPercent: %s", err)) } // ------------- Optional query parameter "cgm.timeInTargetPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInTargetPercent", ctx.QueryParams(), ¶ms.CgmTimeInTargetPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInTargetPercent", ctx.QueryParams(), ¶ms.CgmTimeInTargetPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInTargetPercent: %s", err)) } // ------------- Optional query parameter "cgm.timeInHighPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInHighPercent", ctx.QueryParams(), ¶ms.CgmTimeInHighPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInHighPercent", ctx.QueryParams(), ¶ms.CgmTimeInHighPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInHighPercent: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryHighPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryHighPercent", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryHighPercent", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryHighPercent: %s", err)) } // ------------- Optional query parameter "cgm.timeInExtremeHighPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInExtremeHighPercent", ctx.QueryParams(), ¶ms.CgmTimeInExtremeHighPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInExtremeHighPercent", ctx.QueryParams(), ¶ms.CgmTimeInExtremeHighPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInExtremeHighPercent: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyHighPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyHighPercent", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyHighPercent", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyHighPercent: %s", err)) } // ------------- Optional query parameter "cgm.timeCGMUseMinutes" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeCGMUseMinutes", ctx.QueryParams(), ¶ms.CgmTimeCGMUseMinutes) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeCGMUseMinutes", ctx.QueryParams(), ¶ms.CgmTimeCGMUseMinutes, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeCGMUseMinutes: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryLowMinutes" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryLowMinutes", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowMinutes) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryLowMinutes", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowMinutes, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryLowMinutes: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyLowMinutes" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyLowMinutes", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowMinutes) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyLowMinutes", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowMinutes, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyLowMinutes: %s", err)) } // ------------- Optional query parameter "cgm.timeInLowMinutes" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInLowMinutes", ctx.QueryParams(), ¶ms.CgmTimeInLowMinutes) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInLowMinutes", ctx.QueryParams(), ¶ms.CgmTimeInLowMinutes, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInLowMinutes: %s", err)) } // ------------- Optional query parameter "cgm.timeInTargetMinutes" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInTargetMinutes", ctx.QueryParams(), ¶ms.CgmTimeInTargetMinutes) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInTargetMinutes", ctx.QueryParams(), ¶ms.CgmTimeInTargetMinutes, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInTargetMinutes: %s", err)) } // ------------- Optional query parameter "cgm.timeInHighMinutes" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInHighMinutes", ctx.QueryParams(), ¶ms.CgmTimeInHighMinutes) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInHighMinutes", ctx.QueryParams(), ¶ms.CgmTimeInHighMinutes, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInHighMinutes: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryHighMinutes" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryHighMinutes", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighMinutes) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryHighMinutes", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighMinutes, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryHighMinutes: %s", err)) } // ------------- Optional query parameter "cgm.timeInExtremeHighMinutes" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInExtremeHighMinutes", ctx.QueryParams(), ¶ms.CgmTimeInExtremeHighMinutes) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInExtremeHighMinutes", ctx.QueryParams(), ¶ms.CgmTimeInExtremeHighMinutes, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInExtremeHighMinutes: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyHighMinutes" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyHighMinutes", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighMinutes) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyHighMinutes", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighMinutes, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyHighMinutes: %s", err)) } // ------------- Optional query parameter "cgm.timeCGMUseRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeCGMUseRecords", ctx.QueryParams(), ¶ms.CgmTimeCGMUseRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeCGMUseRecords", ctx.QueryParams(), ¶ms.CgmTimeCGMUseRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeCGMUseRecords: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryLowRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryLowRecords", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryLowRecords", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryLowRecords: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyLowRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyLowRecords", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyLowRecords", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyLowRecords: %s", err)) } // ------------- Optional query parameter "cgm.timeInLowRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInLowRecords", ctx.QueryParams(), ¶ms.CgmTimeInLowRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInLowRecords", ctx.QueryParams(), ¶ms.CgmTimeInLowRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInLowRecords: %s", err)) } // ------------- Optional query parameter "cgm.timeInTargetRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInTargetRecords", ctx.QueryParams(), ¶ms.CgmTimeInTargetRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInTargetRecords", ctx.QueryParams(), ¶ms.CgmTimeInTargetRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInTargetRecords: %s", err)) } // ------------- Optional query parameter "cgm.timeInHighRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInHighRecords", ctx.QueryParams(), ¶ms.CgmTimeInHighRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInHighRecords", ctx.QueryParams(), ¶ms.CgmTimeInHighRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInHighRecords: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryHighRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryHighRecords", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryHighRecords", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryHighRecords: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyHighRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyHighRecords", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyHighRecords", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyHighRecords: %s", err)) } // ------------- Optional query parameter "cgm.averageDailyRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.averageDailyRecords", ctx.QueryParams(), ¶ms.CgmAverageDailyRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.averageDailyRecords", ctx.QueryParams(), ¶ms.CgmAverageDailyRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.averageDailyRecords: %s", err)) } // ------------- Optional query parameter "cgm.totalRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.totalRecords", ctx.QueryParams(), ¶ms.CgmTotalRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.totalRecords", ctx.QueryParams(), ¶ms.CgmTotalRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.totalRecords: %s", err)) } // ------------- Optional query parameter "cgm.hoursWithData" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.hoursWithData", ctx.QueryParams(), ¶ms.CgmHoursWithData) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.hoursWithData", ctx.QueryParams(), ¶ms.CgmHoursWithData, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.hoursWithData: %s", err)) } // ------------- Optional query parameter "cgm.daysWithData" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.daysWithData", ctx.QueryParams(), ¶ms.CgmDaysWithData) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.daysWithData", ctx.QueryParams(), ¶ms.CgmDaysWithData, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.daysWithData: %s", err)) } // ------------- Optional query parameter "cgm.standardDeviation" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.standardDeviation", ctx.QueryParams(), ¶ms.CgmStandardDeviation) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.standardDeviation", ctx.QueryParams(), ¶ms.CgmStandardDeviation, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.standardDeviation: %s", err)) } // ------------- Optional query parameter "cgm.coefficientOfVariation" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.coefficientOfVariation", ctx.QueryParams(), ¶ms.CgmCoefficientOfVariation) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.coefficientOfVariation", ctx.QueryParams(), ¶ms.CgmCoefficientOfVariation, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.coefficientOfVariation: %s", err)) } // ------------- Optional query parameter "bgm.max" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.max", ctx.QueryParams(), ¶ms.BgmMax) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.max", ctx.QueryParams(), ¶ms.BgmMax, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.max: %s", err)) } // ------------- Optional query parameter "bgm.min" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.min", ctx.QueryParams(), ¶ms.BgmMin) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.min", ctx.QueryParams(), ¶ms.BgmMin, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.min: %s", err)) } // ------------- Optional query parameter "bgm.averageGlucoseMmol" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.averageGlucoseMmol", ctx.QueryParams(), ¶ms.BgmAverageGlucoseMmol) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.averageGlucoseMmol", ctx.QueryParams(), ¶ms.BgmAverageGlucoseMmol, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.averageGlucoseMmol: %s", err)) } // ------------- Optional query parameter "bgm.timeInVeryLowPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInVeryLowPercent", ctx.QueryParams(), ¶ms.BgmTimeInVeryLowPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInVeryLowPercent", ctx.QueryParams(), ¶ms.BgmTimeInVeryLowPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInVeryLowPercent: %s", err)) } // ------------- Optional query parameter "bgm.timeInAnyLowPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInAnyLowPercent", ctx.QueryParams(), ¶ms.BgmTimeInAnyLowPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInAnyLowPercent", ctx.QueryParams(), ¶ms.BgmTimeInAnyLowPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInAnyLowPercent: %s", err)) } // ------------- Optional query parameter "bgm.timeInLowPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInLowPercent", ctx.QueryParams(), ¶ms.BgmTimeInLowPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInLowPercent", ctx.QueryParams(), ¶ms.BgmTimeInLowPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInLowPercent: %s", err)) } // ------------- Optional query parameter "bgm.timeInTargetPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInTargetPercent", ctx.QueryParams(), ¶ms.BgmTimeInTargetPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInTargetPercent", ctx.QueryParams(), ¶ms.BgmTimeInTargetPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInTargetPercent: %s", err)) } // ------------- Optional query parameter "bgm.timeInHighPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInHighPercent", ctx.QueryParams(), ¶ms.BgmTimeInHighPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInHighPercent", ctx.QueryParams(), ¶ms.BgmTimeInHighPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInHighPercent: %s", err)) } // ------------- Optional query parameter "bgm.timeInVeryHighPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInVeryHighPercent", ctx.QueryParams(), ¶ms.BgmTimeInVeryHighPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInVeryHighPercent", ctx.QueryParams(), ¶ms.BgmTimeInVeryHighPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInVeryHighPercent: %s", err)) } // ------------- Optional query parameter "bgm.timeInExtremeHighPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInExtremeHighPercent", ctx.QueryParams(), ¶ms.BgmTimeInExtremeHighPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInExtremeHighPercent", ctx.QueryParams(), ¶ms.BgmTimeInExtremeHighPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInExtremeHighPercent: %s", err)) } // ------------- Optional query parameter "bgm.timeInAnyHighPercent" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInAnyHighPercent", ctx.QueryParams(), ¶ms.BgmTimeInAnyHighPercent) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInAnyHighPercent", ctx.QueryParams(), ¶ms.BgmTimeInAnyHighPercent, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInAnyHighPercent: %s", err)) } // ------------- Optional query parameter "bgm.timeInVeryLowRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInVeryLowRecords", ctx.QueryParams(), ¶ms.BgmTimeInVeryLowRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInVeryLowRecords", ctx.QueryParams(), ¶ms.BgmTimeInVeryLowRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInVeryLowRecords: %s", err)) } // ------------- Optional query parameter "bgm.timeInAnyLowRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInAnyLowRecords", ctx.QueryParams(), ¶ms.BgmTimeInAnyLowRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInAnyLowRecords", ctx.QueryParams(), ¶ms.BgmTimeInAnyLowRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInAnyLowRecords: %s", err)) } // ------------- Optional query parameter "bgm.timeInLowRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInLowRecords", ctx.QueryParams(), ¶ms.BgmTimeInLowRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInLowRecords", ctx.QueryParams(), ¶ms.BgmTimeInLowRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInLowRecords: %s", err)) } // ------------- Optional query parameter "bgm.timeInTargetRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInTargetRecords", ctx.QueryParams(), ¶ms.BgmTimeInTargetRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInTargetRecords", ctx.QueryParams(), ¶ms.BgmTimeInTargetRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInTargetRecords: %s", err)) } // ------------- Optional query parameter "bgm.timeInHighRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInHighRecords", ctx.QueryParams(), ¶ms.BgmTimeInHighRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInHighRecords", ctx.QueryParams(), ¶ms.BgmTimeInHighRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInHighRecords: %s", err)) } // ------------- Optional query parameter "bgm.timeInVeryHighRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInVeryHighRecords", ctx.QueryParams(), ¶ms.BgmTimeInVeryHighRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInVeryHighRecords", ctx.QueryParams(), ¶ms.BgmTimeInVeryHighRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInVeryHighRecords: %s", err)) } // ------------- Optional query parameter "bgm.timeInAnyHighRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInAnyHighRecords", ctx.QueryParams(), ¶ms.BgmTimeInAnyHighRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInAnyHighRecords", ctx.QueryParams(), ¶ms.BgmTimeInAnyHighRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInAnyHighRecords: %s", err)) } // ------------- Optional query parameter "bgm.averageDailyRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.averageDailyRecords", ctx.QueryParams(), ¶ms.BgmAverageDailyRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.averageDailyRecords", ctx.QueryParams(), ¶ms.BgmAverageDailyRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.averageDailyRecords: %s", err)) } // ------------- Optional query parameter "bgm.totalRecords" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.totalRecords", ctx.QueryParams(), ¶ms.BgmTotalRecords) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.totalRecords", ctx.QueryParams(), ¶ms.BgmTotalRecords, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.totalRecords: %s", err)) } // ------------- Optional query parameter "cgm.maxDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.maxDelta", ctx.QueryParams(), ¶ms.CgmMaxDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.maxDelta", ctx.QueryParams(), ¶ms.CgmMaxDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.maxDelta: %s", err)) } // ------------- Optional query parameter "cgm.minDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.minDelta", ctx.QueryParams(), ¶ms.CgmMinDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.minDelta", ctx.QueryParams(), ¶ms.CgmMinDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.minDelta: %s", err)) } // ------------- Optional query parameter "cgm.averageGlucoseMmolDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.averageGlucoseMmolDelta", ctx.QueryParams(), ¶ms.CgmAverageGlucoseMmolDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.averageGlucoseMmolDelta", ctx.QueryParams(), ¶ms.CgmAverageGlucoseMmolDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.averageGlucoseMmolDelta: %s", err)) } // ------------- Optional query parameter "cgm.glucoseManagementIndicatorDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.glucoseManagementIndicatorDelta", ctx.QueryParams(), ¶ms.CgmGlucoseManagementIndicatorDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.glucoseManagementIndicatorDelta", ctx.QueryParams(), ¶ms.CgmGlucoseManagementIndicatorDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.glucoseManagementIndicatorDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeCGMUsePercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeCGMUsePercentDelta", ctx.QueryParams(), ¶ms.CgmTimeCGMUsePercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeCGMUsePercentDelta", ctx.QueryParams(), ¶ms.CgmTimeCGMUsePercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeCGMUsePercentDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryLowPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryLowPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryLowPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryLowPercentDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyLowPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyLowPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyLowPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyLowPercentDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInLowPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInLowPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInLowPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInLowPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInLowPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInLowPercentDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInTargetPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInTargetPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInTargetPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInTargetPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInTargetPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInTargetPercentDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInHighPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInHighPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInHighPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInHighPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInHighPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInHighPercentDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryHighPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryHighPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryHighPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryHighPercentDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInExtremeHighPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInExtremeHighPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInExtremeHighPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInExtremeHighPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInExtremeHighPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInExtremeHighPercentDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyHighPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyHighPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyHighPercentDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyHighPercentDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeCGMUseMinutesDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeCGMUseMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeCGMUseMinutesDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeCGMUseMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeCGMUseMinutesDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeCGMUseMinutesDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryLowMinutesDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryLowMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowMinutesDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryLowMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowMinutesDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryLowMinutesDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyLowMinutesDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyLowMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowMinutesDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyLowMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowMinutesDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyLowMinutesDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInLowMinutesDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInLowMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInLowMinutesDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInLowMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInLowMinutesDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInLowMinutesDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInTargetMinutesDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInTargetMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInTargetMinutesDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInTargetMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInTargetMinutesDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInTargetMinutesDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInHighMinutesDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInHighMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInHighMinutesDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInHighMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInHighMinutesDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInHighMinutesDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryHighMinutesDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryHighMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighMinutesDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryHighMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighMinutesDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryHighMinutesDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInExtremeHighMinutesDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInExtremeHighMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInExtremeHighMinutesDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInExtremeHighMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInExtremeHighMinutesDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInExtremeHighMinutesDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyHighMinutesDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyHighMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighMinutesDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyHighMinutesDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighMinutesDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyHighMinutesDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeCGMUseRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeCGMUseRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeCGMUseRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeCGMUseRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeCGMUseRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeCGMUseRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryLowRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryLowRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryLowRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryLowRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryLowRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyLowRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyLowRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyLowRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyLowRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyLowRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInLowRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInLowRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInLowRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInLowRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInLowRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInLowRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInTargetRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInTargetRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInTargetRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInTargetRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInTargetRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInTargetRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInHighRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInHighRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInHighRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInHighRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInHighRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInHighRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInVeryHighRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInVeryHighRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInVeryHighRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInVeryHighRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInVeryHighRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.timeInAnyHighRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.timeInAnyHighRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.timeInAnyHighRecordsDelta", ctx.QueryParams(), ¶ms.CgmTimeInAnyHighRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.timeInAnyHighRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.averageDailyRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.averageDailyRecordsDelta", ctx.QueryParams(), ¶ms.CgmAverageDailyRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.averageDailyRecordsDelta", ctx.QueryParams(), ¶ms.CgmAverageDailyRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.averageDailyRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.totalRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.totalRecordsDelta", ctx.QueryParams(), ¶ms.CgmTotalRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.totalRecordsDelta", ctx.QueryParams(), ¶ms.CgmTotalRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.totalRecordsDelta: %s", err)) } // ------------- Optional query parameter "cgm.hoursWithDataDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.hoursWithDataDelta", ctx.QueryParams(), ¶ms.CgmHoursWithDataDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.hoursWithDataDelta", ctx.QueryParams(), ¶ms.CgmHoursWithDataDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.hoursWithDataDelta: %s", err)) } // ------------- Optional query parameter "cgm.daysWithDataDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.daysWithDataDelta", ctx.QueryParams(), ¶ms.CgmDaysWithDataDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.daysWithDataDelta", ctx.QueryParams(), ¶ms.CgmDaysWithDataDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.daysWithDataDelta: %s", err)) } // ------------- Optional query parameter "cgm.standardDeviationDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.standardDeviationDelta", ctx.QueryParams(), ¶ms.CgmStandardDeviationDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.standardDeviationDelta", ctx.QueryParams(), ¶ms.CgmStandardDeviationDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.standardDeviationDelta: %s", err)) } // ------------- Optional query parameter "cgm.coefficientOfVariationDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.coefficientOfVariationDelta", ctx.QueryParams(), ¶ms.CgmCoefficientOfVariationDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.coefficientOfVariationDelta", ctx.QueryParams(), ¶ms.CgmCoefficientOfVariationDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.coefficientOfVariationDelta: %s", err)) } // ------------- Optional query parameter "cgm.lastDataFrom" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.lastDataFrom", ctx.QueryParams(), ¶ms.CgmLastDataFrom) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.lastDataFrom", ctx.QueryParams(), ¶ms.CgmLastDataFrom, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.lastDataFrom: %s", err)) } // ------------- Optional query parameter "cgm.lastDataTo" ------------- - err = runtime.BindQueryParameter("form", true, false, "cgm.lastDataTo", ctx.QueryParams(), ¶ms.CgmLastDataTo) + err = runtime.BindQueryParameterWithOptions("form", true, false, "cgm.lastDataTo", ctx.QueryParams(), ¶ms.CgmLastDataTo, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cgm.lastDataTo: %s", err)) } // ------------- Optional query parameter "bgm.maxDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.maxDelta", ctx.QueryParams(), ¶ms.BgmMaxDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.maxDelta", ctx.QueryParams(), ¶ms.BgmMaxDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.maxDelta: %s", err)) } // ------------- Optional query parameter "bgm.minDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.minDelta", ctx.QueryParams(), ¶ms.BgmMinDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.minDelta", ctx.QueryParams(), ¶ms.BgmMinDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.minDelta: %s", err)) } // ------------- Optional query parameter "bgm.averageGlucoseMmolDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.averageGlucoseMmolDelta", ctx.QueryParams(), ¶ms.BgmAverageGlucoseMmolDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.averageGlucoseMmolDelta", ctx.QueryParams(), ¶ms.BgmAverageGlucoseMmolDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.averageGlucoseMmolDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInVeryLowPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInVeryLowPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInVeryLowPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInVeryLowPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInVeryLowPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInVeryLowPercentDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInAnyLowPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInAnyLowPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInAnyLowPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInAnyLowPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInAnyLowPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInAnyLowPercentDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInLowPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInLowPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInLowPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInLowPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInLowPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInLowPercentDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInTargetPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInTargetPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInTargetPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInTargetPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInTargetPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInTargetPercentDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInHighPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInHighPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInHighPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInHighPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInHighPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInHighPercentDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInVeryHighPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInVeryHighPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInVeryHighPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInVeryHighPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInVeryHighPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInVeryHighPercentDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInExtremeHighPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInExtremeHighPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInExtremeHighPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInExtremeHighPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInExtremeHighPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInExtremeHighPercentDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInAnyHighPercentDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInAnyHighPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInAnyHighPercentDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInAnyHighPercentDelta", ctx.QueryParams(), ¶ms.BgmTimeInAnyHighPercentDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInAnyHighPercentDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInVeryLowRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInVeryLowRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInVeryLowRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInVeryLowRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInVeryLowRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInVeryLowRecordsDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInAnyLowRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInAnyLowRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInAnyLowRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInAnyLowRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInAnyLowRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInAnyLowRecordsDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInLowRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInLowRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInLowRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInLowRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInLowRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInLowRecordsDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInTargetRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInTargetRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInTargetRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInTargetRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInTargetRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInTargetRecordsDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInHighRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInHighRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInHighRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInHighRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInHighRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInHighRecordsDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInVeryHighRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInVeryHighRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInVeryHighRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInVeryHighRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInVeryHighRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInVeryHighRecordsDelta: %s", err)) } // ------------- Optional query parameter "bgm.timeInAnyHighRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.timeInAnyHighRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInAnyHighRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.timeInAnyHighRecordsDelta", ctx.QueryParams(), ¶ms.BgmTimeInAnyHighRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.timeInAnyHighRecordsDelta: %s", err)) } // ------------- Optional query parameter "bgm.averageDailyRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.averageDailyRecordsDelta", ctx.QueryParams(), ¶ms.BgmAverageDailyRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.averageDailyRecordsDelta", ctx.QueryParams(), ¶ms.BgmAverageDailyRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.averageDailyRecordsDelta: %s", err)) } // ------------- Optional query parameter "bgm.totalRecordsDelta" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.totalRecordsDelta", ctx.QueryParams(), ¶ms.BgmTotalRecordsDelta) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.totalRecordsDelta", ctx.QueryParams(), ¶ms.BgmTotalRecordsDelta, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.totalRecordsDelta: %s", err)) } // ------------- Optional query parameter "bgm.lastDataFrom" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.lastDataFrom", ctx.QueryParams(), ¶ms.BgmLastDataFrom) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.lastDataFrom", ctx.QueryParams(), ¶ms.BgmLastDataFrom, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.lastDataFrom: %s", err)) } // ------------- Optional query parameter "bgm.lastDataTo" ------------- - err = runtime.BindQueryParameter("form", true, false, "bgm.lastDataTo", ctx.QueryParams(), ¶ms.BgmLastDataTo) + err = runtime.BindQueryParameterWithOptions("form", true, false, "bgm.lastDataTo", ctx.QueryParams(), ¶ms.BgmLastDataTo, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter bgm.lastDataTo: %s", err)) } // ------------- Optional query parameter "tags" ------------- - err = runtime.BindQueryParameter("form", false, false, "tags", ctx.QueryParams(), ¶ms.Tags) + err = runtime.BindQueryParameterWithOptions("form", false, false, "tags", ctx.QueryParams(), ¶ms.Tags, runtime.BindQueryParameterOptions{Type: "array", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter tags: %s", err)) } // ------------- Optional query parameter "sites" ------------- - err = runtime.BindQueryParameter("form", false, false, "sites", ctx.QueryParams(), ¶ms.Sites) + err = runtime.BindQueryParameterWithOptions("form", false, false, "sites", ctx.QueryParams(), ¶ms.Sites, runtime.BindQueryParameterOptions{Type: "array", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter sites: %s", err)) } // ------------- Optional query parameter "omitNonStandardRanges" ------------- - err = runtime.BindQueryParameter("form", true, false, "omitNonStandardRanges", ctx.QueryParams(), ¶ms.OmitNonStandardRanges) + err = runtime.BindQueryParameterWithOptions("form", true, false, "omitNonStandardRanges", ctx.QueryParams(), ¶ms.OmitNonStandardRanges, runtime.BindQueryParameterOptions{Type: "boolean", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter omitNonStandardRanges: %s", err)) } @@ -1929,13 +1863,11 @@ func (w *ServerInterfaceWrapper) CreatePatientAccount(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.CreatePatientAccount(ctx, clinicId) return err @@ -1947,7 +1879,7 @@ func (w *ServerInterfaceWrapper) AssignPatientTagToClinicPatients(ctx echo.Conte // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -1955,13 +1887,11 @@ func (w *ServerInterfaceWrapper) AssignPatientTagToClinicPatients(ctx echo.Conte // ------------- Path parameter "patientTagId" ------------- var patientTagId PatientTagId - err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientTagId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.AssignPatientTagToClinicPatients(ctx, clinicId, patientTagId) return err @@ -1973,7 +1903,7 @@ func (w *ServerInterfaceWrapper) DeletePatientTagFromClinicPatients(ctx echo.Con // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -1981,13 +1911,11 @@ func (w *ServerInterfaceWrapper) DeletePatientTagFromClinicPatients(ctx echo.Con // ------------- Path parameter "patientTagId" ------------- var patientTagId PatientTagId - err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientTagId", ctx.Param("patientTagId"), &patientTagId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientTagId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeletePatientTagFromClinicPatients(ctx, clinicId, patientTagId) return err @@ -1999,7 +1927,7 @@ func (w *ServerInterfaceWrapper) DeletePatient(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2007,13 +1935,11 @@ func (w *ServerInterfaceWrapper) DeletePatient(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeletePatient(ctx, clinicId, patientId) return err @@ -2025,7 +1951,7 @@ func (w *ServerInterfaceWrapper) GetPatient(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2033,13 +1959,11 @@ func (w *ServerInterfaceWrapper) GetPatient(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetPatient(ctx, clinicId, patientId) return err @@ -2051,7 +1975,7 @@ func (w *ServerInterfaceWrapper) CreatePatientFromUser(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2059,13 +1983,11 @@ func (w *ServerInterfaceWrapper) CreatePatientFromUser(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.CreatePatientFromUser(ctx, clinicId, patientId) return err @@ -2077,7 +1999,7 @@ func (w *ServerInterfaceWrapper) UpdatePatient(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2085,13 +2007,11 @@ func (w *ServerInterfaceWrapper) UpdatePatient(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdatePatient(ctx, clinicId, patientId) return err @@ -2103,7 +2023,7 @@ func (w *ServerInterfaceWrapper) ConnectProvider(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2111,7 +2031,7 @@ func (w *ServerInterfaceWrapper) ConnectProvider(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } @@ -2119,13 +2039,11 @@ func (w *ServerInterfaceWrapper) ConnectProvider(ctx echo.Context) error { // ------------- Path parameter "providerId" ------------- var providerId ProviderId - err = runtime.BindStyledParameterWithOptions("simple", "providerId", ctx.Param("providerId"), &providerId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "providerId", ctx.Param("providerId"), &providerId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter providerId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.ConnectProvider(ctx, clinicId, patientId, providerId) return err @@ -2137,7 +2055,7 @@ func (w *ServerInterfaceWrapper) UpdatePatientPermissions(ctx echo.Context) erro // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2145,13 +2063,11 @@ func (w *ServerInterfaceWrapper) UpdatePatientPermissions(ctx echo.Context) erro // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdatePatientPermissions(ctx, clinicId, patientId) return err @@ -2163,7 +2079,7 @@ func (w *ServerInterfaceWrapper) DeletePatientPermission(ctx echo.Context) error // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2171,7 +2087,7 @@ func (w *ServerInterfaceWrapper) DeletePatientPermission(ctx echo.Context) error // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } @@ -2179,13 +2095,11 @@ func (w *ServerInterfaceWrapper) DeletePatientPermission(ctx echo.Context) error // ------------- Path parameter "permission" ------------- var permission string - err = runtime.BindStyledParameterWithOptions("simple", "permission", ctx.Param("permission"), &permission, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "permission", ctx.Param("permission"), &permission, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter permission: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeletePatientPermission(ctx, clinicId, patientId, permission) return err @@ -2197,7 +2111,7 @@ func (w *ServerInterfaceWrapper) DeletePatientReviews(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2205,13 +2119,11 @@ func (w *ServerInterfaceWrapper) DeletePatientReviews(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeletePatientReviews(ctx, clinicId, patientId) return err @@ -2223,7 +2135,7 @@ func (w *ServerInterfaceWrapper) UpdatePatientReviews(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2231,13 +2143,11 @@ func (w *ServerInterfaceWrapper) UpdatePatientReviews(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdatePatientReviews(ctx, clinicId, patientId) return err @@ -2249,7 +2159,7 @@ func (w *ServerInterfaceWrapper) SendUploadReminder(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2257,13 +2167,11 @@ func (w *ServerInterfaceWrapper) SendUploadReminder(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.SendUploadReminder(ctx, clinicId, patientId) return err @@ -2275,13 +2183,11 @@ func (w *ServerInterfaceWrapper) GenerateMergeReport(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GenerateMergeReport(ctx, clinicId) return err @@ -2293,13 +2199,11 @@ func (w *ServerInterfaceWrapper) AddServiceAccount(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.AddServiceAccount(ctx, clinicId) return err @@ -2311,13 +2215,11 @@ func (w *ServerInterfaceWrapper) GetEHRSettings(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetEHRSettings(ctx, clinicId) return err @@ -2329,13 +2231,11 @@ func (w *ServerInterfaceWrapper) UpdateEHRSettings(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateEHRSettings(ctx, clinicId) return err @@ -2347,13 +2247,11 @@ func (w *ServerInterfaceWrapper) GetMRNSettings(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetMRNSettings(ctx, clinicId) return err @@ -2365,13 +2263,11 @@ func (w *ServerInterfaceWrapper) UpdateMRNSettings(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateMRNSettings(ctx, clinicId) return err @@ -2383,13 +2279,11 @@ func (w *ServerInterfaceWrapper) GetPatientCountSettings(ctx echo.Context) error // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.GetPatientCountSettings(ctx, clinicId) return err @@ -2401,13 +2295,11 @@ func (w *ServerInterfaceWrapper) UpdatePatientCountSettings(ctx echo.Context) er // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdatePatientCountSettings(ctx, clinicId) return err @@ -2419,13 +2311,11 @@ func (w *ServerInterfaceWrapper) CreateSite(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.CreateSite(ctx, clinicId) return err @@ -2437,7 +2327,7 @@ func (w *ServerInterfaceWrapper) DeleteSite(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2445,13 +2335,11 @@ func (w *ServerInterfaceWrapper) DeleteSite(ctx echo.Context) error { // ------------- Path parameter "siteId" ------------- var siteId SiteId - err = runtime.BindStyledParameterWithOptions("simple", "siteId", ctx.Param("siteId"), &siteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "siteId", ctx.Param("siteId"), &siteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter siteId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeleteSite(ctx, clinicId, siteId) return err @@ -2463,7 +2351,7 @@ func (w *ServerInterfaceWrapper) UpdateSite(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2471,13 +2359,11 @@ func (w *ServerInterfaceWrapper) UpdateSite(ctx echo.Context) error { // ------------- Path parameter "siteId" ------------- var siteId SiteId - err = runtime.BindStyledParameterWithOptions("simple", "siteId", ctx.Param("siteId"), &siteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "siteId", ctx.Param("siteId"), &siteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter siteId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateSite(ctx, clinicId, siteId) return err @@ -2489,7 +2375,7 @@ func (w *ServerInterfaceWrapper) MergeSite(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } @@ -2497,13 +2383,11 @@ func (w *ServerInterfaceWrapper) MergeSite(ctx echo.Context) error { // ------------- Path parameter "siteId" ------------- var siteId SiteId - err = runtime.BindStyledParameterWithOptions("simple", "siteId", ctx.Param("siteId"), &siteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "siteId", ctx.Param("siteId"), &siteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter siteId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.MergeSite(ctx, clinicId, siteId) return err @@ -2515,13 +2399,11 @@ func (w *ServerInterfaceWrapper) UpdateSuppressedNotifications(ctx echo.Context) // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateSuppressedNotifications(ctx, clinicId) return err @@ -2533,50 +2415,55 @@ func (w *ServerInterfaceWrapper) TideReport(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params TideReportParams // ------------- Required query parameter "period" ------------- - err = runtime.BindQueryParameter("form", true, true, "period", ctx.QueryParams(), ¶ms.Period) + err = runtime.BindQueryParameterWithOptions("form", true, true, "period", ctx.QueryParams(), ¶ms.Period, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter period: %s", err)) } // ------------- Required query parameter "tags" ------------- - err = runtime.BindQueryParameter("form", false, true, "tags", ctx.QueryParams(), ¶ms.Tags) + err = runtime.BindQueryParameterWithOptions("form", false, true, "tags", ctx.QueryParams(), ¶ms.Tags, runtime.BindQueryParameterOptions{Type: "array", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter tags: %s", err)) } // ------------- Required query parameter "lastDataCutoff" ------------- - err = runtime.BindQueryParameter("form", true, true, "lastDataCutoff", ctx.QueryParams(), ¶ms.LastDataCutoff) + err = runtime.BindQueryParameterWithOptions("form", true, true, "lastDataCutoff", ctx.QueryParams(), ¶ms.LastDataCutoff, runtime.BindQueryParameterOptions{Type: "string", Format: "date-time"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter lastDataCutoff: %s", err)) } // ------------- Optional query parameter "categories" ------------- - err = runtime.BindQueryParameter("form", false, false, "categories", ctx.QueryParams(), ¶ms.Categories) + err = runtime.BindQueryParameterWithOptions("form", false, false, "categories", ctx.QueryParams(), ¶ms.Categories, runtime.BindQueryParameterOptions{Type: "array", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter categories: %s", err)) } // ------------- Optional query parameter "excludeNoData" ------------- - err = runtime.BindQueryParameter("form", true, false, "excludeNoData", ctx.QueryParams(), ¶ms.ExcludeNoData) + err = runtime.BindQueryParameterWithOptions("form", true, false, "excludeNoData", ctx.QueryParams(), ¶ms.ExcludeNoData, runtime.BindQueryParameterOptions{Type: "boolean", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter excludeNoData: %s", err)) } + // ------------- Optional query parameter "sites" ------------- + + err = runtime.BindQueryParameterWithOptions("form", false, false, "sites", ctx.QueryParams(), ¶ms.Sites, runtime.BindQueryParameterOptions{Type: "array", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter sites: %s", err)) + } + // Invoke the callback with all the unmarshaled arguments err = w.Handler.TideReport(ctx, clinicId, params) return err @@ -2588,13 +2475,11 @@ func (w *ServerInterfaceWrapper) UpdateTier(ctx echo.Context) error { // ------------- Path parameter "clinicId" ------------- var clinicId ClinicId - err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "clinicId", ctx.Param("clinicId"), &clinicId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateTier(ctx, clinicId) return err @@ -2604,48 +2489,46 @@ func (w *ServerInterfaceWrapper) UpdateTier(ctx echo.Context) error { func (w *ServerInterfaceWrapper) FindPatients(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params FindPatientsParams // ------------- Optional query parameter "mrn" ------------- - err = runtime.BindQueryParameter("form", true, false, "mrn", ctx.QueryParams(), ¶ms.Mrn) + err = runtime.BindQueryParameterWithOptions("form", true, false, "mrn", ctx.QueryParams(), ¶ms.Mrn, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter mrn: %s", err)) } // ------------- Optional query parameter "birthDate" ------------- - err = runtime.BindQueryParameter("form", true, false, "birthDate", ctx.QueryParams(), ¶ms.BirthDate) + err = runtime.BindQueryParameterWithOptions("form", true, false, "birthDate", ctx.QueryParams(), ¶ms.BirthDate, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter birthDate: %s", err)) } // ------------- Optional query parameter "workspaceId" ------------- - err = runtime.BindQueryParameter("form", true, false, "workspaceId", ctx.QueryParams(), ¶ms.WorkspaceId) + err = runtime.BindQueryParameterWithOptions("form", true, false, "workspaceId", ctx.QueryParams(), ¶ms.WorkspaceId, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter workspaceId: %s", err)) } // ------------- Optional query parameter "workspaceIdType" ------------- - err = runtime.BindQueryParameter("form", true, false, "workspaceIdType", ctx.QueryParams(), ¶ms.WorkspaceIdType) + err = runtime.BindQueryParameterWithOptions("form", true, false, "workspaceIdType", ctx.QueryParams(), ¶ms.WorkspaceIdType, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter workspaceIdType: %s", err)) } // ------------- Optional query parameter "offset" ------------- - err = runtime.BindQueryParameter("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset) + err = runtime.BindQueryParameterWithOptions("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter offset: %s", err)) } // ------------- Optional query parameter "limit" ------------- - err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + err = runtime.BindQueryParameterWithOptions("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } @@ -2661,13 +2544,11 @@ func (w *ServerInterfaceWrapper) SyncEHRDataForPatient(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.SyncEHRDataForPatient(ctx, patientId) return err @@ -2679,13 +2560,11 @@ func (w *ServerInterfaceWrapper) UpdatePatientSummary(ctx echo.Context) error { // ------------- Path parameter "patientId" ------------- var patientId PatientId - err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "patientId", ctx.Param("patientId"), &patientId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdatePatientSummary(ctx, patientId) return err @@ -2697,25 +2576,23 @@ func (w *ServerInterfaceWrapper) ListClinicsForPatient(ctx echo.Context) error { // ------------- Path parameter "userId" ------------- var userId UserId - err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params ListClinicsForPatientParams // ------------- Optional query parameter "offset" ------------- - err = runtime.BindQueryParameter("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset) + err = runtime.BindQueryParameterWithOptions("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter offset: %s", err)) } // ------------- Optional query parameter "limit" ------------- - err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + err = runtime.BindQueryParameterWithOptions("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } @@ -2731,13 +2608,11 @@ func (w *ServerInterfaceWrapper) UpdatePatientDataSources(ctx echo.Context) erro // ------------- Path parameter "userId" ------------- var userId UserId - err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdatePatientDataSources(ctx, userId) return err @@ -2747,8 +2622,6 @@ func (w *ServerInterfaceWrapper) UpdatePatientDataSources(ctx echo.Context) erro func (w *ServerInterfaceWrapper) ProcessEHRMessage(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.ProcessEHRMessage(ctx) return err @@ -2758,8 +2631,6 @@ func (w *ServerInterfaceWrapper) ProcessEHRMessage(ctx echo.Context) error { func (w *ServerInterfaceWrapper) MatchClinicAndPatient(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.MatchClinicAndPatient(ctx) return err @@ -2769,8 +2640,6 @@ func (w *ServerInterfaceWrapper) MatchClinicAndPatient(ctx echo.Context) error { func (w *ServerInterfaceWrapper) VerifyEndpoint(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.VerifyEndpoint(ctx) return err @@ -2782,13 +2651,11 @@ func (w *ServerInterfaceWrapper) DeletePatientSummary(ctx echo.Context) error { // ------------- Path parameter "summaryId" ------------- var summaryId SummaryId - err = runtime.BindStyledParameterWithOptions("simple", "summaryId", ctx.Param("summaryId"), &summaryId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "summaryId", ctx.Param("summaryId"), &summaryId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter summaryId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeletePatientSummary(ctx, summaryId) return err @@ -2800,13 +2667,11 @@ func (w *ServerInterfaceWrapper) DeleteUserFromClinics(ctx echo.Context) error { // ------------- Path parameter "userId" ------------- var userId UserId - err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.DeleteUserFromClinics(ctx, userId) return err @@ -2818,13 +2683,11 @@ func (w *ServerInterfaceWrapper) UpdateClinicUserDetails(ctx echo.Context) error // ------------- Path parameter "userId" ------------- var userId UserId - err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "userId", ctx.Param("userId"), &userId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) } - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UpdateClinicUserDetails(ctx, userId) return err @@ -2834,8 +2697,6 @@ func (w *ServerInterfaceWrapper) UpdateClinicUserDetails(ctx echo.Context) error func (w *ServerInterfaceWrapper) XealthNotification(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.XealthNotification(ctx) return err @@ -2845,8 +2706,6 @@ func (w *ServerInterfaceWrapper) XealthNotification(ctx echo.Context) error { func (w *ServerInterfaceWrapper) XealthPreorder(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.XealthPreorder(ctx) return err @@ -2856,8 +2715,6 @@ func (w *ServerInterfaceWrapper) XealthPreorder(ctx echo.Context) error { func (w *ServerInterfaceWrapper) XealthGetProgramUrl(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.XealthGetProgramUrl(ctx) return err @@ -2867,8 +2724,6 @@ func (w *ServerInterfaceWrapper) XealthGetProgramUrl(ctx echo.Context) error { func (w *ServerInterfaceWrapper) XealthGetPrograms(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.XealthGetPrograms(ctx) return err @@ -2878,27 +2733,25 @@ func (w *ServerInterfaceWrapper) XealthGetPrograms(ctx echo.Context) error { func (w *ServerInterfaceWrapper) ViewPDFReport(ctx echo.Context) error { var err error - ctx.Set(SessionTokenScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params ViewPDFReportParams // ------------- Required query parameter "clinicId" ------------- - err = runtime.BindQueryParameter("form", true, true, "clinicId", ctx.QueryParams(), ¶ms.ClinicId) + err = runtime.BindQueryParameterWithOptions("form", true, true, "clinicId", ctx.QueryParams(), ¶ms.ClinicId, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter clinicId: %s", err)) } // ------------- Required query parameter "patientId" ------------- - err = runtime.BindQueryParameter("form", true, true, "patientId", ctx.QueryParams(), ¶ms.PatientId) + err = runtime.BindQueryParameterWithOptions("form", true, true, "patientId", ctx.QueryParams(), ¶ms.PatientId, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter patientId: %s", err)) } // ------------- Required query parameter "restricted_token" ------------- - err = runtime.BindQueryParameter("form", true, true, "restricted_token", ctx.QueryParams(), ¶ms.RestrictedToken) + err = runtime.BindQueryParameterWithOptions("form", true, true, "restricted_token", ctx.QueryParams(), ¶ms.RestrictedToken, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter restricted_token: %s", err)) } @@ -2923,95 +2776,114 @@ type EchoRouter interface { TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route } +// RegisterHandlersOptions configures RegisterHandlersWithOptions. +type RegisterHandlersOptions struct { + // BaseURL is prepended to every registered path so the API can be served + // under a prefix. + BaseURL string + // OperationMiddlewares lets the caller attach per-operation middleware at + // registration time. The map key is the OpenAPI `operationId` value as it + // appears in the spec (the raw, un-normalized form). Operations that have + // no entry are registered with no extra middleware. A nil map disables + // per-operation middleware entirely. + OperationMiddlewares map[string][]echo.MiddlewareFunc +} + // RegisterHandlers adds each server route to the EchoRouter. func RegisterHandlers(router EchoRouter, si ServerInterface) { - RegisterHandlersWithBaseURL(router, si, "") + RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{}) } -// Registers handlers, and prepends BaseURL to the paths, so that the paths -// can be served under a prefix. +// RegisterHandlersWithBaseURL registers handlers and prepends BaseURL to the +// paths so the API can be served under a prefix. func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{BaseURL: baseURL}) +} + +// RegisterHandlersWithOptions registers handlers using the supplied options, +// including any per-operation middleware. +func RegisterHandlersWithOptions(router EchoRouter, si ServerInterface, options RegisterHandlersOptions) { wrapper := ServerInterfaceWrapper{ Handler: si, } - router.GET(baseURL+"/v1/clinicians", wrapper.ListAllClinicians) - router.GET(baseURL+"/v1/clinicians/:userId/clinics", wrapper.ListClinicsForClinician) - router.POST(baseURL+"/v1/clinicians/:userId/migrate", wrapper.EnableNewClinicExperience) - router.GET(baseURL+"/v1/clinics", wrapper.ListClinics) - router.POST(baseURL+"/v1/clinics", wrapper.CreateClinic) - router.GET(baseURL+"/v1/clinics/share_code/:shareCode", wrapper.GetClinicByShareCode) - router.DELETE(baseURL+"/v1/clinics/:clinicId", wrapper.DeleteClinic) - router.GET(baseURL+"/v1/clinics/:clinicId", wrapper.GetClinic) - router.PUT(baseURL+"/v1/clinics/:clinicId", wrapper.UpdateClinic) - router.GET(baseURL+"/v1/clinics/:clinicId/clinicians", wrapper.ListClinicians) - router.POST(baseURL+"/v1/clinics/:clinicId/clinicians", wrapper.CreateClinician) - router.DELETE(baseURL+"/v1/clinics/:clinicId/clinicians/:clinicianId", wrapper.DeleteClinician) - router.GET(baseURL+"/v1/clinics/:clinicId/clinicians/:clinicianId", wrapper.GetClinician) - router.PUT(baseURL+"/v1/clinics/:clinicId/clinicians/:clinicianId", wrapper.UpdateClinician) - router.POST(baseURL+"/v1/clinics/:clinicId/ehr/sync", wrapper.SyncEHRData) - router.DELETE(baseURL+"/v1/clinics/:clinicId/invites/clinicians/:inviteId/clinician", wrapper.DeleteInvitedClinician) - router.GET(baseURL+"/v1/clinics/:clinicId/invites/clinicians/:inviteId/clinician", wrapper.GetInvitedClinician) - router.PATCH(baseURL+"/v1/clinics/:clinicId/invites/clinicians/:inviteId/clinician", wrapper.AssociateClinicianToUser) - router.GET(baseURL+"/v1/clinics/:clinicId/membership_restrictions", wrapper.ListMembershipRestrictions) - router.PUT(baseURL+"/v1/clinics/:clinicId/membership_restrictions", wrapper.UpdateMembershipRestrictions) - router.POST(baseURL+"/v1/clinics/:clinicId/merge", wrapper.MergeClinic) - router.POST(baseURL+"/v1/clinics/:clinicId/migrate", wrapper.TriggerInitialMigration) - router.GET(baseURL+"/v1/clinics/:clinicId/migrations", wrapper.ListMigrations) - router.POST(baseURL+"/v1/clinics/:clinicId/migrations", wrapper.MigrateLegacyClinicianPatients) - router.GET(baseURL+"/v1/clinics/:clinicId/migrations/:userId", wrapper.GetMigration) - router.PATCH(baseURL+"/v1/clinics/:clinicId/migrations/:userId", wrapper.UpdateMigration) - router.GET(baseURL+"/v1/clinics/:clinicId/patient_count", wrapper.GetPatientCount) - router.POST(baseURL+"/v1/clinics/:clinicId/patient_count/refresh", wrapper.RefreshPatientCount) - router.POST(baseURL+"/v1/clinics/:clinicId/patient_tags", wrapper.CreatePatientTag) - router.DELETE(baseURL+"/v1/clinics/:clinicId/patient_tags/:patientTagId", wrapper.DeletePatientTag) - router.PUT(baseURL+"/v1/clinics/:clinicId/patient_tags/:patientTagId", wrapper.UpdatePatientTag) - router.POST(baseURL+"/v1/clinics/:clinicId/patient_tags/:patientTagId/site", wrapper.ConvertPatientTagToSite) - router.GET(baseURL+"/v1/clinics/:clinicId/patients", wrapper.ListPatients) - router.POST(baseURL+"/v1/clinics/:clinicId/patients", wrapper.CreatePatientAccount) - router.POST(baseURL+"/v1/clinics/:clinicId/patients/assign_tag/:patientTagId", wrapper.AssignPatientTagToClinicPatients) - router.POST(baseURL+"/v1/clinics/:clinicId/patients/delete_tag/:patientTagId", wrapper.DeletePatientTagFromClinicPatients) - router.DELETE(baseURL+"/v1/clinics/:clinicId/patients/:patientId", wrapper.DeletePatient) - router.GET(baseURL+"/v1/clinics/:clinicId/patients/:patientId", wrapper.GetPatient) - router.POST(baseURL+"/v1/clinics/:clinicId/patients/:patientId", wrapper.CreatePatientFromUser) - router.PUT(baseURL+"/v1/clinics/:clinicId/patients/:patientId", wrapper.UpdatePatient) - router.POST(baseURL+"/v1/clinics/:clinicId/patients/:patientId/connect/:providerId", wrapper.ConnectProvider) - router.PUT(baseURL+"/v1/clinics/:clinicId/patients/:patientId/permissions", wrapper.UpdatePatientPermissions) - router.DELETE(baseURL+"/v1/clinics/:clinicId/patients/:patientId/permissions/:permission", wrapper.DeletePatientPermission) - router.DELETE(baseURL+"/v1/clinics/:clinicId/patients/:patientId/reviews", wrapper.DeletePatientReviews) - router.PUT(baseURL+"/v1/clinics/:clinicId/patients/:patientId/reviews", wrapper.UpdatePatientReviews) - router.POST(baseURL+"/v1/clinics/:clinicId/patients/:patientId/upload_reminder", wrapper.SendUploadReminder) - router.POST(baseURL+"/v1/clinics/:clinicId/reports/merge", wrapper.GenerateMergeReport) - router.POST(baseURL+"/v1/clinics/:clinicId/service_accounts", wrapper.AddServiceAccount) - router.GET(baseURL+"/v1/clinics/:clinicId/settings/ehr", wrapper.GetEHRSettings) - router.PUT(baseURL+"/v1/clinics/:clinicId/settings/ehr", wrapper.UpdateEHRSettings) - router.GET(baseURL+"/v1/clinics/:clinicId/settings/mrn", wrapper.GetMRNSettings) - router.PUT(baseURL+"/v1/clinics/:clinicId/settings/mrn", wrapper.UpdateMRNSettings) - router.GET(baseURL+"/v1/clinics/:clinicId/settings/patient_count", wrapper.GetPatientCountSettings) - router.PUT(baseURL+"/v1/clinics/:clinicId/settings/patient_count", wrapper.UpdatePatientCountSettings) - router.POST(baseURL+"/v1/clinics/:clinicId/sites", wrapper.CreateSite) - router.DELETE(baseURL+"/v1/clinics/:clinicId/sites/:siteId", wrapper.DeleteSite) - router.PUT(baseURL+"/v1/clinics/:clinicId/sites/:siteId", wrapper.UpdateSite) - router.POST(baseURL+"/v1/clinics/:clinicId/sites/:siteId/merge", wrapper.MergeSite) - router.POST(baseURL+"/v1/clinics/:clinicId/suppressed_notifications", wrapper.UpdateSuppressedNotifications) - router.GET(baseURL+"/v1/clinics/:clinicId/tide_report", wrapper.TideReport) - router.POST(baseURL+"/v1/clinics/:clinicId/tier", wrapper.UpdateTier) - router.GET(baseURL+"/v1/patients", wrapper.FindPatients) - router.POST(baseURL+"/v1/patients/:patientId/ehr/sync", wrapper.SyncEHRDataForPatient) - router.POST(baseURL+"/v1/patients/:patientId/summary", wrapper.UpdatePatientSummary) - router.GET(baseURL+"/v1/patients/:userId/clinics", wrapper.ListClinicsForPatient) - router.PUT(baseURL+"/v1/patients/:userId/data_sources", wrapper.UpdatePatientDataSources) - router.POST(baseURL+"/v1/redox", wrapper.ProcessEHRMessage) - router.POST(baseURL+"/v1/redox/match", wrapper.MatchClinicAndPatient) - router.POST(baseURL+"/v1/redox/verify", wrapper.VerifyEndpoint) - router.DELETE(baseURL+"/v1/summaries/:summaryId/clinics", wrapper.DeletePatientSummary) - router.DELETE(baseURL+"/v1/users/:userId/clinics", wrapper.DeleteUserFromClinics) - router.POST(baseURL+"/v1/users/:userId/clinics", wrapper.UpdateClinicUserDetails) - router.POST(baseURL+"/v1/xealth/notification", wrapper.XealthNotification) - router.POST(baseURL+"/v1/xealth/preorder", wrapper.XealthPreorder) - router.PUT(baseURL+"/v1/xealth/program", wrapper.XealthGetProgramUrl) - router.PUT(baseURL+"/v1/xealth/programs", wrapper.XealthGetPrograms) - router.GET(baseURL+"/v1/xealth/report/web/viewer.html", wrapper.ViewPDFReport) + router.GET(options.BaseURL+"/v1/clinics", wrapper.ListClinics, options.OperationMiddlewares["ListClinics"]...) + router.POST(options.BaseURL+"/v1/clinics", wrapper.CreateClinic, options.OperationMiddlewares["CreateClinic"]...) + router.GET(options.BaseURL+"/v1/clinics/share_code/:shareCode", wrapper.GetClinicByShareCode, options.OperationMiddlewares["GetClinicByShareCode"]...) + router.DELETE(options.BaseURL+"/v1/clinics/:clinicId", wrapper.DeleteClinic, options.OperationMiddlewares["DeleteClinic"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId", wrapper.GetClinic, options.OperationMiddlewares["GetClinic"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId", wrapper.UpdateClinic, options.OperationMiddlewares["UpdateClinic"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/clinicians", wrapper.ListClinicians, options.OperationMiddlewares["ListClinicians"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/clinicians", wrapper.CreateClinician, options.OperationMiddlewares["CreateClinician"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/tide_report", wrapper.TideReport, options.OperationMiddlewares["TideReport"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/patients", wrapper.ListPatients, options.OperationMiddlewares["ListPatients"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/patients", wrapper.CreatePatientAccount, options.OperationMiddlewares["CreatePatientAccount"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/patient_tags", wrapper.CreatePatientTag, options.OperationMiddlewares["CreatePatientTag"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/sites", wrapper.CreateSite, options.OperationMiddlewares["CreateSite"]...) + router.DELETE(options.BaseURL+"/v1/clinics/:clinicId/sites/:siteId", wrapper.DeleteSite, options.OperationMiddlewares["DeleteSite"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/sites/:siteId", wrapper.UpdateSite, options.OperationMiddlewares["UpdateSite"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/sites/:siteId/merge", wrapper.MergeSite, options.OperationMiddlewares["MergeSite"]...) + router.DELETE(options.BaseURL+"/v1/clinics/:clinicId/clinicians/:clinicianId", wrapper.DeleteClinician, options.OperationMiddlewares["DeleteClinician"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/clinicians/:clinicianId", wrapper.GetClinician, options.OperationMiddlewares["GetClinician"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/clinicians/:clinicianId", wrapper.UpdateClinician, options.OperationMiddlewares["UpdateClinician"]...) + router.DELETE(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId", wrapper.DeletePatient, options.OperationMiddlewares["DeletePatient"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId", wrapper.GetPatient, options.OperationMiddlewares["GetPatient"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId", wrapper.CreatePatientFromUser, options.OperationMiddlewares["CreatePatientFromUser"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId", wrapper.UpdatePatient, options.OperationMiddlewares["UpdatePatient"]...) + router.DELETE(options.BaseURL+"/v1/clinics/:clinicId/patient_tags/:patientTagId", wrapper.DeletePatientTag, options.OperationMiddlewares["DeletePatientTag"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/patient_tags/:patientTagId", wrapper.UpdatePatientTag, options.OperationMiddlewares["UpdatePatientTag"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/patient_tags/:patientTagId/site", wrapper.ConvertPatientTagToSite, options.OperationMiddlewares["ConvertPatientTagToSite"]...) + router.DELETE(options.BaseURL+"/v1/clinics/:clinicId/invites/clinicians/:inviteId/clinician", wrapper.DeleteInvitedClinician, options.OperationMiddlewares["DeleteInvitedClinician"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/invites/clinicians/:inviteId/clinician", wrapper.GetInvitedClinician, options.OperationMiddlewares["GetInvitedClinician"]...) + router.PATCH(options.BaseURL+"/v1/clinics/:clinicId/invites/clinicians/:inviteId/clinician", wrapper.AssociateClinicianToUser, options.OperationMiddlewares["AssociateClinicianToUser"]...) + router.DELETE(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId/reviews", wrapper.DeletePatientReviews, options.OperationMiddlewares["DeletePatientReviews"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId/reviews", wrapper.UpdatePatientReviews, options.OperationMiddlewares["UpdatePatientReviews"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId/permissions", wrapper.UpdatePatientPermissions, options.OperationMiddlewares["UpdatePatientPermissions"]...) + router.DELETE(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId/permissions/:permission", wrapper.DeletePatientPermission, options.OperationMiddlewares["DeletePatientPermission"]...) + router.GET(options.BaseURL+"/v1/patients/:userId/clinics", wrapper.ListClinicsForPatient, options.OperationMiddlewares["ListClinicsForPatient"]...) + router.GET(options.BaseURL+"/v1/clinicians/:userId/clinics", wrapper.ListClinicsForClinician, options.OperationMiddlewares["ListClinicsForClinician"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/migrations", wrapper.ListMigrations, options.OperationMiddlewares["ListMigrations"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/migrations", wrapper.MigrateLegacyClinicianPatients, options.OperationMiddlewares["MigrateLegacyClinicianPatients"]...) + router.POST(options.BaseURL+"/v1/clinicians/:userId/migrate", wrapper.EnableNewClinicExperience, options.OperationMiddlewares["EnableNewClinicExperience"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/migrate", wrapper.TriggerInitialMigration, options.OperationMiddlewares["TriggerInitialMigration"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/migrations/:userId", wrapper.GetMigration, options.OperationMiddlewares["GetMigration"]...) + router.PATCH(options.BaseURL+"/v1/clinics/:clinicId/migrations/:userId", wrapper.UpdateMigration, options.OperationMiddlewares["UpdateMigration"]...) + router.DELETE(options.BaseURL+"/v1/users/:userId/clinics", wrapper.DeleteUserFromClinics, options.OperationMiddlewares["DeleteUserFromClinics"]...) + router.POST(options.BaseURL+"/v1/users/:userId/clinics", wrapper.UpdateClinicUserDetails, options.OperationMiddlewares["UpdateClinicUserDetails"]...) + router.GET(options.BaseURL+"/v1/clinicians", wrapper.ListAllClinicians, options.OperationMiddlewares["ListAllClinicians"]...) + router.POST(options.BaseURL+"/v1/patients/:patientId/summary", wrapper.UpdatePatientSummary, options.OperationMiddlewares["UpdatePatientSummary"]...) + router.DELETE(options.BaseURL+"/v1/summaries/:summaryId/clinics", wrapper.DeletePatientSummary, options.OperationMiddlewares["DeletePatientSummary"]...) + router.PUT(options.BaseURL+"/v1/patients/:userId/data_sources", wrapper.UpdatePatientDataSources, options.OperationMiddlewares["UpdatePatientDataSources"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/tier", wrapper.UpdateTier, options.OperationMiddlewares["UpdateTier"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/suppressed_notifications", wrapper.UpdateSuppressedNotifications, options.OperationMiddlewares["UpdateSuppressedNotifications"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId/upload_reminder", wrapper.SendUploadReminder, options.OperationMiddlewares["SendUploadReminder"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/patients/assign_tag/:patientTagId", wrapper.AssignPatientTagToClinicPatients, options.OperationMiddlewares["AssignPatientTagToClinicPatients"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/patients/delete_tag/:patientTagId", wrapper.DeletePatientTagFromClinicPatients, options.OperationMiddlewares["DeletePatientTagFromClinicPatients"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/membership_restrictions", wrapper.ListMembershipRestrictions, options.OperationMiddlewares["ListMembershipRestrictions"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/membership_restrictions", wrapper.UpdateMembershipRestrictions, options.OperationMiddlewares["UpdateMembershipRestrictions"]...) + router.POST(options.BaseURL+"/v1/redox", wrapper.ProcessEHRMessage, options.OperationMiddlewares["ProcessEHRMessage"]...) + router.POST(options.BaseURL+"/v1/redox/verify", wrapper.VerifyEndpoint, options.OperationMiddlewares["VerifyEndpoint"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/settings/ehr", wrapper.GetEHRSettings, options.OperationMiddlewares["GetEHRSettings"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/settings/ehr", wrapper.UpdateEHRSettings, options.OperationMiddlewares["UpdateEHRSettings"]...) + router.POST(options.BaseURL+"/v1/redox/match", wrapper.MatchClinicAndPatient, options.OperationMiddlewares["MatchClinicAndPatient"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/settings/mrn", wrapper.GetMRNSettings, options.OperationMiddlewares["GetMRNSettings"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/settings/mrn", wrapper.UpdateMRNSettings, options.OperationMiddlewares["UpdateMRNSettings"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/settings/patient_count", wrapper.GetPatientCountSettings, options.OperationMiddlewares["GetPatientCountSettings"]...) + router.PUT(options.BaseURL+"/v1/clinics/:clinicId/settings/patient_count", wrapper.UpdatePatientCountSettings, options.OperationMiddlewares["UpdatePatientCountSettings"]...) + router.GET(options.BaseURL+"/v1/clinics/:clinicId/patient_count", wrapper.GetPatientCount, options.OperationMiddlewares["GetPatientCount"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/patient_count/refresh", wrapper.RefreshPatientCount, options.OperationMiddlewares["RefreshPatientCount"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/ehr/sync", wrapper.SyncEHRData, options.OperationMiddlewares["SyncEHRData"]...) + router.GET(options.BaseURL+"/v1/patients", wrapper.FindPatients, options.OperationMiddlewares["FindPatients"]...) + router.POST(options.BaseURL+"/v1/xealth/preorder", wrapper.XealthPreorder, options.OperationMiddlewares["XealthPreorder"]...) + router.PUT(options.BaseURL+"/v1/xealth/programs", wrapper.XealthGetPrograms, options.OperationMiddlewares["XealthGetPrograms"]...) + router.PUT(options.BaseURL+"/v1/xealth/program", wrapper.XealthGetProgramUrl, options.OperationMiddlewares["XealthGetProgramUrl"]...) + router.POST(options.BaseURL+"/v1/xealth/notification", wrapper.XealthNotification, options.OperationMiddlewares["XealthNotification"]...) + router.GET(options.BaseURL+"/v1/xealth/report/web/viewer.html", wrapper.ViewPDFReport, options.OperationMiddlewares["ViewPDFReport"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/service_accounts", wrapper.AddServiceAccount, options.OperationMiddlewares["AddServiceAccount"]...) + router.POST(options.BaseURL+"/v1/patients/:patientId/ehr/sync", wrapper.SyncEHRDataForPatient, options.OperationMiddlewares["SyncEHRDataForPatient"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/reports/merge", wrapper.GenerateMergeReport, options.OperationMiddlewares["GenerateMergeReport"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/merge", wrapper.MergeClinic, options.OperationMiddlewares["MergeClinic"]...) + router.POST(options.BaseURL+"/v1/clinics/:clinicId/patients/:patientId/connect/:providerId", wrapper.ConnectProvider, options.OperationMiddlewares["ConnectProvider"]...) } diff --git a/api/gen_spec.go b/api/gen_spec.go index 73654c0ea..7e6744824 100644 --- a/api/gen_spec.go +++ b/api/gen_spec.go @@ -1,11 +1,11 @@ // Package api provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.8.0 DO NOT EDIT. package api import ( "bytes" - "compress/gzip" + "compress/flate" "encoding/base64" "fmt" "net/url" @@ -15,370 +15,381 @@ import ( "github.com/getkin/kin-openapi/openapi3" ) -// Base64 encoded, gzipped, json marshaled Swagger object +// Base64 encoded, compressed with deflate, json marshaled OpenAPI spec. +// Stored as a slice of fixed-width chunks rather than one concatenated +// const string: with thousands of chunks the chained `+` fold is several +// times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - - "H4sIAAAAAAAC/+y963LbuLIo/CoonV21ZvaybPmSyaXq1P4c20k8sR1vXzJnrUmOBiRbImISYABQtjLj", - "qvMQ35/v9c6TfIULSfAmUbLkeO2dH3FEEAQajUaj0ejLnz2fxQmjQKXovfqzl2COY5DA9ZMfEUr840D9", - "JrT3qpdgGfY2ehTH0HtVvN7ocfiaEg5B75XkKWz0hB9CjE2LUgJXH//v33F/NOi//Pznzt79v/U2enKa", - "qGaE5ISOe/f3G3mLm5Nt9W0AwuckkYSp7w/0S3R82NtYGpp/4zDqver9j61i2FvmrdhyOy+AIZjOQYCp", - "0REHP/0+6L/E/dHnP7cH93/lDy/u+/nvvQ6/t3fuf25BIQcsIbgiMRzRoI7FC5App4iDz3ggkK2OPBgx", - "DkiGgMZkAhQFWAL6Ce78KBVkAj9nSP+aAp86KCh35456xHiMZe9VTzXVlySGeQBfSsxlZ5DxSAKvQUxo", - "d4hNf0vADCE/otiLoBnBnMAEkKEOgW6JDBGY6ujo3QUiVMKYY12/GUanfRc6C4jHWASYGkhiTKKcPKvN", - "6JeN48te1cdG6IRIaKX5/PUsgo8JPQE6lmHv1XZTHxGJiWyD2rx0mwtghNNI9l5tDzZU2yROY7dljVDg", - "umk2Gglobdu+rcBq2hs0tpdgSYDKVnwU758KB7AQXeHxPKBNlZVx74SzCQmAHzesinP7rpV9Ox8vy8CL", - "JjIWzlkEbaSg3zWsLmdAAjD3w/pg3qRRhCTcSWRqoKzppn5sI3N6CjGHAxa0QltUmNPQrLUr5q/cWfhl", - "3hfwZYFdwZq49SXjEjGu5hrTAGEpOfFSvZlsjjfR3xUsiHHUVz/amLRuupkQf/qPV/2/Pn36+88//cer", - "33H/237/n5//Gv7890aSFGkcYz5tx0j+flmk5C1kWElFtgIa+rMvl+1MkgASxiLVDAl693qI5p369E3E", - "sHxDIglcPcIdjhO1AHqf0sFgF/7ns81nvY0SL7Iv/jL/m/98++j//Pvf+5//46efPn0K/v7Tp0+bnz4F", - "//7zf/z8l/3995+bGNBG75jOguH54hCovhp7uk7UNn2ZJgkHISA4Y5KMiK93ViPOcpYAlwT0k2ivOHuG", - "Gz/LeUw+k7+39vB5oyeJ1GiYDXM+SLPWikFeEYPP8oikLZ1NM8CbYNXf1gDT/TRAgYPgEviE+LDv+yyl", - "0oroZXj8SG0rQxLMEwI2end9IVkSkXFoNuug96p3Mx5NMU7i0RfVnpXBVYMCfG429cUbffElYIIOZOR9", - "TZ7pRs1aXKat3d3d3bH4Nv26e/f8pVl+Lkp1wxsOFqoDcNC9X0VoDestIPjPb9iLwSCOgl0zqVgI5hMs", - "4SA7jlyxa2EmvTZDBXeqM0t3KLaeC3DWDcr7aSIUbxyfAyes+Qx3abglYiOEkUjAV7SPXr89RUrWRon+", - "Ev3kp5wDldH0FdoONtDzYANt7wUbaHcQqB2jPCQ8AY7HcIhJNL0wZ4R6x/umEgpULcQBB4SO1XrLGdTu", - "5rN8ODSNPb0O7vpj1reFI8Vdf9nTq6He5SFEEtf7PSSjEXCgPiAP5C0A1SeWhgYQUa+IyJCgNs8JcEEY", - "Na8AsSRhag9HuRibQ7+zOPRvo9RnAk5jFrXjy1ZSE8Yb4XSBePYgIJbCYAZfA/I0xmQI3KIrw+ayaPMZ", - "jEbEV+v5w+gj5sSc4NxN7uHN5UhYqs0AT8VvRIaHuNrI7CYIldXPGwDp1EaIxX7zgqyeXt26FVpsrKpO", - "7Md0n07fkXF4DtwHKrtVngdEXvmE3XZu+ITddmv36E5yiKE71M4H3Xro3nT3NjujojMerjAfg+zWqKnb", - "rd2PwBcgiax297Y7Y8JWntsykziaWSnGd6WV96IzA4jxXX3hLvA5KfOz3UW+rHfc/XMhMQ0wDw5h8kC2", - "WmvpYRxVtjCdinbDvFAbJhsZUUYkQKXalvbpFIVkHKKx3ao4pmNwN6HB5u7DAFps52xq4WHCx2BzZzn4", - "W8W1AyUR653bFTvmo3J70GmTagLiAUhciQT3bDHIy1xpMXKM2O2qqbEAZ0k8Fg18B1osc+2upDgTjYtS", - "YgHC8gh8dDpsFm26EyOY71fPH+uALYPVeiuPTZvNomAXAu2G2oWotA7MA5H66PS6NKGunEAfSJnfkSSX", - "oMUV0uADie+7UN2yW/Wqt+mH7dHfb4NefHde3c78sG35e+zJtWN2d4KT+tOV0lwJmmVQWGrgsSmvpobo", - "QnzzsLgQ/ZUgWB5/j06FDUqZ7nQ4Ab6GM3MFpGWQWWniscmxQXfVhSA7oHMhmqyA8RBEfhe6XHY/1nhc", - "9aZcBmhZXH6/zbmu9OxMkqvbpctAPACJj0mPFTXwPLQtgxyniwWR4nz5MGR0ArVy+RwT2nNUykYh7ii3", - "226OWi8bWm8WWi5RWm5MZlw6zLhhmH0VNPvep/3iq/2Wq/kSp/nGpv2qo/1eo/XKrPV+rH7t0XpPWLnD", - "bLqTdGwSvHGc5MYGs4wRhLVGwEFAFOXj6LxkQzDLgKZk0XC/Ub0oRzFOkGQIsB8ikSYJ4xICbdNgbcOK", - "m/Ia3KIJcGeB9MUNSfosMTD3E0aoNqySPAUzvkuJpWi0tdjP+9fWFqkALhQzUZuEgi5jvRMcpSBqZhU+", - "oyMy7mj+dqArZwjCEkTHDw9VXfudMRvqbmy30bNI7DyDudGWMxEiQ2AD/fhLGLMc/DBm+WHM8sOY5b+E", - "MYtlkaeY4jHEQOUxDYiPJeNN7DYATiYQGIaKbkPihwjiNFIsDu1vH7hz9XwBRLRDsRiVtbezLMUtMor1", - "GQa9nTlLrQYbB29PrwWcEpra7WpOzS5GIKZmZ3sjJbZ1AWD9Zk+doVi3iVQ3QB7HpqobLGuyvurW+brM", - "tLr1vm6Trm5QPIIBWHdAHt9aLGQpf8hGWfp+2Z3yh8nakzJZq+1trdql2NSxWs5bwJzQMcLIH8cl2XLQ", - "XR9X6nxxZVzp88fSDNe2+I5q4VaELawKLkGwLNa+ixK4JvQ8lNi2F6W1pRW/pc8f1X6vsr11RFknI8hF", - "1moVlgdYQT7yiv1hlfvDKvcpWuUuvahnXoctvKYLSJa3J/0eK/qHXfMPu+anZNe81HLuZny72Jquw/RA", - "69tHX90/rMV/WIv/q1mLL7X8V7nsH7jeV7bQdwY/7Ox/2Nk/dTv7pZbrCgXvh0nd30Pk/uGc8MM54bGd", - "E5ZapfPM6hdbqCVAlrerf/yt9Yd7xw/3jqfj3rHUSu7gj7DYYq5A8xCHhEfff384yvxwlHnCjjLLL/AV", - "StVlWB7g3vFdVvcPd6Mf7kY/3I2esLvRDGeiGaal3Z2NMh76QC+khmaWd05qaGwVfksNzc72amr1UGpo", - "aUm/pZahLuXR1NBWk6tTN4et5sYaEVU2T2oxYW6xQl7I+6pu1dZqn9bqRDDbHaBqsNhogNjBDWxjrpug", - "4yjmz3EU81fhKOYv5Sh2MMtRzF+Ro5i/rKPYwX8rRzG/1VHMn+MoRuS0OWkNkVNEcQyb7i7VO8cRQ/uR", - "ZIaA3TDUtkP1YVPMc5NVZEaCnLpLWRBwEPPxLDmA3DeVLdp8TE/JmGOp42VzwMEHGk2zmPV1U2SFhrk4", - "tqjKx3JJvunmgaax2o4H/Z29l72N3s6zQX/vpfr1bDDov9S/tgeDwd/1wm7Gy5UuLtrKUkIME459SXwl", - "84WAIxn6mMNQTIWEuLfRm4AETijm06GfYVE7v+hQ/SaFgwmXPjO1ieJ8KZV8PhJMtQwPRUqa1iQ0Lcgv", - "ep6/KEoJjjZ6ERbqAAASgvM8H8jcHBt5TdtKFkx91kea/u1CzL/XBEkkxGLhPu3YMed4qhsNGYUzLe7N", - "b8ypm8HEhMRRln1j5sd5zexTDiPgHILX42tKTA6tjPbi8VZwotZ3zKKtk0aiLaX9mLk4s4q2XyX9dkeg", - "qt2Euu77h+J984FUlTIAV59rYSNPd1DwUVUy2B4Mamx07nJRXx667NNt9g0HWKbJGL4xCt3W4ZWtbceW", - "6vQLD+QBt+CpuS41kHLSmBrFPQHpVAU2e4Gbacbh/2UuVYa3jk07Vw0LxBHK8v2qvp12yANHAqCKRICX", - "t9adEezsvXixs/0cYG8Xtr0deLHr74yMZJjN585eaXrV44xcRzXEl0Zgchq17UjONDvMYX/EiY+39j0S", - "fNGJFLIC3+fYeQwCIob7HvbcwmhMFKcrCkSMS1+JGNzn1zjGN8x9puOUlJ6/pJHzTITAqfMcYSqnHJwS", - "jr99wxMSRW5h+iWNvdTt+QATztxHgb0IU9+tAql0HxnFNzoVkC04xDeYu498CGJ4iSOMY6f4C/FYKp1B", - "HbIUR07DR9FwH5PUwbVa5ZLdOiVvsce4WsF5yTvMsTvwX1mIKQXhpXzslKbu/LzHcVLq+n2IuWSpA+57", - "MsYRcZ+pCLFwvjnBY+ZM8QnxOFTwfcJi9ylVpzT32UtjD4uQuGUC3zh1TnGEPeY+J6ksPQvgDiGcKkJ0", - "0XPKxjggInTrMKqELqeXM0UEngPGWfAFx0DdKgTH4Ez6GUvxjR8yKYuyDyke44ClY+b0ds64ZP0zNnGg", - "vsRseFXCzRWJvfRGOt9dcZIwdwauUkocfP9GaBAyuFElMdi1iEuP1A+ZOkGXysYpiczU50WSjNNSCcfj", - "FBNaLhsrbkbVIgLKxHCfcH3Uqlc4wBLHmPvNnx+wmAUXZIIDbOegoQoPmNf87tf0SzptfHOChxeEfWn+", - "7BRowL41v7sgbPgWRxFYeq5VuMTm9N70hg5/TQ17bHx5kpLmNq9SP41bPrwWYYoruEnL+BAp9c02lhdJ", - "csNuyi3KG/ej1zgktefha0wD4FiUXnAPByVkvIZIH0OcZ3UocgoU0+xfYi8qQfWa4eFHIkroe83GrFJA", - "RKmtZgo7wLHHSTCG4Ws8LZcnbPiWq4GUiqmf0lIBxz4ut1in1AM8BUrLDU3LM3UQEh+PWbkkTHFYWkUH", - "JA1woMiDwze3nHEcDd9h7rGUl8srVH+ghPnhBSnDx0HIEo4PUoLL36VqoC58h5jGmN+IEE9oqfhWsHrB", - "8IBDibEcAp0ALxVIznQOzLyExUqYcME4CmJGy6AeEZ5SSFzsHkVqq5zggLkdHFEBFAduc28Yl8MziMoQ", - "69Lf8LQ0Y6oQR1Ba728j7Fcp5y0LZIi9UgkTtVqKsoZXKb8pFVbhe5viACKWlkb3NsUSYhxVKk7x11Rn", - "Ny3KprjEb9/hiIy0trAomVSqAI+ZIFHkzvQxDQim+f9qCxENr99TdtdQfIo50HFTe+c653ImVFReXkEU", - "Da0+qPruI0xwYzmhvlpoTdD9RiiOsV9/Ux9OOiHutBx/xVFaIsxfcYzLdFndQn5NKRhh0ha8BypT/2a6", - "dcJSInKZpvr2lFFJfCjjXyF2eHzmlnAcAQ3IFxfOEzw8xy5XOCGxC+OJ4n90DFEJP43wnLBb4MNzrvDp", - "Vj7FPhBWKqC4vNGrkrT8DSdjJsslklDyNYVSocQx46z86TcsoxKfrG+6p0AVn4BSY8BJUK4kI3yjGisV", - "3hGfVYnsVAFW3nFOGfVltUSqE960WjYhAbBKIQccVYoEcI5dnJzh7PSRFcDt8B+sxB/OSELGJTDOrMCX", - "P3JGQ1wukeHwEN8wqTbYNMJh29sD0MqPlrcKnEtc3rDP0tQF78MXQvHY7f0cqzVXLhhTwmVKx6VSrrZM", - "4rmIOw8ZUOIyFCX19nHaN2RZeTFko+FlggmtlLPhvs+hVvgRorDUWwqq+IL45VIq8XBfsWWXLC8wodPh", - "BSnvXxeY3hA6PKYRuBN7AT4ZQalgXBaDL0CwKJWlOoQNX3NMS9BcMIF5afVdYgXfscAeRNViXpoqVUTK", - "8oUqYkO9x1bK2fAcpyUOdOkzDsKbilSnO8+LQ5Jw5rtEcEnKAuKlHL7GXIZK1puWy39lIRXlovdEykrR", - "SeqTSoNXIYtxpZph/S7iL2/JSA4PTBw6p/wKxqmvTqKJ2+xVmJY44FWYKhm2sm1fkS9pecO8UktOsnKJ", - "ZCU+81FNZFqmlo+Ej0vE+ltIJISMl6TW3wilJAF3sfwD36SyxDr+obaL2xtqyUzNvS+t4sEwqKLoEE/M", - "ZucUpUqkOrzm+SZQvDvF/tcUc1IrzmQ8p8w/TXnAyoXnOIrNsIuyC33FgMuFlyyV4fCcVQG4nLLbStUr", - "zqKoXPSRCck0FeqCrRNGx1PA3JuChlIQdZB1fkcxNjxfP8VWFNcPFAdTnj99lYYp2wfmQf4kwjH2zETY", - "55sQezjIC+SUFx+/xuMwKF6+xiG3zMo83jg16fjGjMY8coqNnko/AuFp3ulrIsIbKOoqSZhkTwc48lNp", - "TkX6OSTuAyMejkQx8oOQ0fFXkwbWFqR0fOMWsIjFhkmrx0Ps+7h4iLHwzcavn0Orc9EPJMqhOkw97DyI", - "ENMCqW9wjMepKMB8i7/lv9XxpkDZO/A4K57Y8CAkw1NCw6KIjofvWQH+OzbJ8X/Mb1IpcsQdC4mpV2D5", - "V3yDeQHFr3iKE6vi08/AU5FthqrgPXY+fo9jP8SyGP57dUgMSfGoSIcXjzKMMQ1Sp6D8HGIaTMdFcyy6", - "wQVw7zkWlE0xL4bzPsURHp6kcZIW3aR+6Mzl+/QWk5yOTrOzXfaQFg9jHBREcopvlKDCi2dKohyU01T4", - "xYo4Iz4TJH95xibsJv1GwcG7KhPEIw7sH2LnN8c5Vs9DyuLhORQTfK5kZUxxXv18qtY9Lgb5n1gWoP6n", - "OvlSnC/7/5x+m0aMBzmAF5iOWUFSF2SKg7yzS5yJXubpJsQRcZ7VURjTnL4ugRUEcRliOg4Lqr8kdIwT", - "xnOyv+QQULhh0dQZ/BUmSbGYr7Ba6TRH7pVHIiKK1xDyYpauIBruT8gkfw5J7KXuUxIWj+xmyooHB4Lr", - "LykdD88xDRycXkcYUw+7mL2OMB2+tvZhpoSn8dccuGsh+2dQLJ+PBPTM5eP/GOGATHImror0NiecR+qg", - "/x9wg/XVdXZ2NIUcJgYH6syg9oH9b8yqe7KS18Dj1Ex5VnSAKTaq96IkgeFH4Eb1k5W+wcBZpaRS8Cum", - "w1NsN52s8BQHQHipywuY3nzB9pSZFZot8C0wPial2pdy+A4iqzkuCjGNzO6eCslxpHacg6vycwARJmYU", - "eeFrTkSmznYK2Q3Q4Ttidta8/EAxZ26ALwpTbiWCvOgQ81uzGvKio9SPyt+9Yx7mslR08u64/ExoAHY3", - "LgoZD4bv2G25y1OIPJbyykDOLn8rP6szTKnkHKol/5kCUBHZ1ZsX6/kol0wDWkH5FRYxpqQ80I/El4xX", - "Cn8DUR77P5RUeEuontfXHH8j0ZY9q9inQygOdLboCOtW7JNt8+BIzfvB5dUvB4f6F6Y4UAKIoZWiRB3x", - "DEu1Bao54LQoOGXqyEOckjO4HbGUBhY/tvQc67jPRcElFjdY+iHcYufjf6Q3etUehCSCrQPFsClQaUDQ", - "ZQaC4wz9B0YnfaRHdHRp/z470uM6Gk8TNd4jorF0JP2tt6dXxa+/D5zf2+7v0ovSmx3nwf296/zec34/", - "c37/4vx+7vx+4fx+WfzuO1D0t93fpRelNzvuw6774ADVd2u5ldw6DuB9B/C+A3jfAbzvAJ6DxwHoLfFD", - "+3x9kCH/+uog+0XVsVjoGVbP/0wjtdMcpZwlsLUfq9kO9B1mVkQDZjhMVqAWyE2oySgrkiHoo6J9fg3R", - "yCyEomDMseZ0eQk3+3P2zLEkIsIT7JalQkDkNpz6IeZQajoNcFIpEYSOwWn8ICSCUOwM9IAlQENcqnWY", - "eiWQ3hKP48jso1lRCpyaQ5steQeRIPSGFCXHIoIhGw1PXQw5Aqwt+RV4qaH3Sl4hVKHJKSQwcZ84cx+n", - "xHk6IcJjTo8nX1Iv+mIOw1kRo0GpSnoHsWLS46LsFAecBO6zuQfLHzmBEMdOK6eEajkge2QUa7VI/ix8", - "dls8F1KnLfggIqf6OebEmfBzFowZN7rcrIjjcepQ0gUZO28vjMbNPmm5D7vPSgDghDK3jOMvMKmUSBfT", - "lyQeAWcJc+bv8oYlX9yu2Mgd1aVk/k3IImclXeEoItTB3BXhZqN3nkWpk+toiimbuPi9/haOGWfOFH3E", - "QfrNfVRnbqcbJc65ZPCRRJSkDpI/smjMyoT3G+YCO7P2Tzzm4LnPCePsWzh1wP9nyg3vefta/+nbfcDs", - "ARn/zxit5Vsuz3qn9xN1Lrwxx8JjH+y+Y+4CtvapOiJQzImeL1t6EFqzhPyZEyGNCiorYn6pBosZd1p4", - "D3ycKhmuKDrFIbhPUUAmINySlBNp8JgXTZmUzlcXkFJzY3tspP9jwbFWBRY3FL/iRL96f4u/4Ag0Azoh", - "3lS9O9Xb7Oml/fv8VG+zRi2+9Rp/wUp8gnLRpT5S2oK3QMEIFGf/1H/6B+/2VRtneIK/KAScX6id4fzy", - "6sW5btwKDlv7iabm/DH1b+xUZEWvWTrGhGZaqaz4IMQy1BtIUWL00NmzESncgpGx5cqfaQDcS7XQn5W9", - "wTeYjZhbQr4Q9zGleGRsbbKitzjCiSWNoiz2SKn3tykOcORjqvHklLpjeMcoi8xWmRVp9ai5dciK3mNa", - "KSCKRmJcAus9U1TgFjhTn5Wd4i8pZ6UC/jUFgd3BnJLgFrtYOsMpd2E8I6nb0RnjIxbdlErSGNyJPsdj", - "Njw3muiiLMJuq+dE+phwF9xzFlJzGi5KKE6gVMDl8NToqZ3iC8yZZHTsAnGJiVkURUHM3ApXOCQlnF5h", - "jm9LNVSTEicu3Fe8RIe/4RsoPUbmojEr+AdO1BPL6J5xmY41kVx8ONB/3/c2eq6y4CrlN3pPN5LX9eXW", - "fqTk7uw3pOpYbZ84+caofVUI/teXen307eVnUWKOAdeXW+/wLSbE/La1+pcScz2Y68utU+KHZJx14xwY", - "ri+dY8H1ZY5UIxy6guFv/ctr9Z9mP1pCrBlIosyEsGZkuNG76wvJkoiMQ+1cSoLeq97zUKa3HE9S9kJQ", - "beppLBEJpo32lFchoD9I8AeK8RR5gCBO5BSRkXa1yz9Fiv9IQCEWiDKJPACKsO9DIiHYrDuFrMKkHWJM", - "otLnpqQJD4WL3yIm8ZIEkDAWpQKULKa+0cM8DprxRALtp6t+GXSQESJSIYX+rYKTJpPQzFi+3rB6kzWd", - "47w3192As6iDX1LW3oWqvTp744oRcTY3BqZZ9sI1EjdjbTEDJpiaWhcQGXPwkCSWkitEZwyKO6Gj5IKi", - "+u+KxMwzyB26O2MWiKYx1oex4Kgz/61OVv9zsHffCUDRq7tb1OnJBSmzcD44OT47PhjuH55qew37eHp0", - "+vroQktDR5cHF8fqockzIib02LS43QAnOgceEyG0S0HNqSHVBhX288wBLvt0ORS2OJ6Y90s02dZe4RbU", - "7KLNm5zJri/3293IrD9Sk326Xp/W9adxPWEpQUhNC5epFxOp2FpjDgqPcO2bCWU/ip3B9k5/8KK/q84f", - "JRbTBFBA8JgyQUTmxTULj6XKFp2jNIrOWlmselvis9avaD6XHUdTH2LiX2A6ns9uy7Uzl0VhHSlaEBjB", - "GPvTnMCPl9i5Yk6bBx5DQHwcWc9+ZPziF0ZC4iy5bg5bziL9Hm5LqhqLVU+JnPZejXAk8rJvwFnOG6R1", - "Ruvmg3Zcdw490CsJneeorPH0AEt8yVLuQ+NCK143CR6Xeg4Qh4SDAGoWpPHV5SD0Z0grklbu33KIJUYG", - "MPRBDwYdNwo2cJcQruHKBIqZaxdLUCJG7oio2Izqa4mPYxaQESkEmQU+zZxDzzq4MLp1M2LOfOJyZ1Og", - "gRFH7a8L8BmlxlPb/tJyYUCE+wicM+1bWjBO93VpMux02/eKDgwY84SzrFZpzI6MYmkXBUUns+l4oU2v", - "TP4NG587MzX6//3izQHa3d19+fmnUMpEvNraur293SQgR5uMj7f4yFf/VI1NeSd/Rlvo9+PLD+jFL4Pt", - "yieC6S+IYH31tq+lXUwDLfH2zQ61Gco4+lkHahESxwka3hIZDlHm3ocINRUryfDUbve8P9jpD365Guy8", - "2n3+au+Xf1b3vUy0diYVtjInuk6HmtrG51Cgqpz5p+/Y/3fVGXSc7eP60JlwCAj2wASNiIyhdcyCqeP1", - "TJncT5KI+NjTSqcyfeYdZcPIgEJ6727iECE/BCEJLTw6a2xwFLFbEQI0BEpyvkXHh4hNgHMSABoxjt5k", - "n4n5uxhljdGsZjV/xgye5h3FQKSRXLDtC/vRnNYri7nAU9FtNjRnTR/CCKeRRBcQsDsUlMHANEAJ8H7M", - "AohyoERjjIm6juHbL/4X/m1nAM/iSarhg5Dn83AJUhI6bp5k4lt/5yqWpBIXqD5Sm0gTSIQsjQLkARI2", - "1BT2fcYx9QGpNYmOD1gAO4izVPNblEUqERuIcdXQLWSNCLA5LW3bWOpIb8BRwsEnSkrZ7NVjGlQdZTXw", - "Do7zMaNs0E1sE0J+iqUfnoIQeAwXMGoVA07VfLhr+gMPTOSBOidgfqpjErkypbPiJkpgqQREOIPbhrYq", - "oyzgcFspdeig4OjdRWVwHYkIb+9sP5PPJiTZlTwjIt3UBXxNQTQfSuKimzm7TjPWiyAEomsDFhy7R4oP", - "SeGUfl/CA9LVka3fiAbLQ1Xf981ooV8HcRQ/C74Go9tBE1oa4KirQjiRwAluDDmndiSBMEWfeh8uPvVQ", - "rKFW/EjHplFbehZqK5O4a8f70wt1qj+9OBsefnjd2+gdfng9fHN9cnK2f3pUp7DmkcZjsn2z82XKvvh7", - "N3qk5WN/Veivt7DNAj7e/fri27eARLoFRq/18V8jrD76D/aogLCRniRT7CUxOIEA3YZAEUZGhWARE2Jh", - "VHva+kOtCYuDo7P91ydHw4uj8w8XV5cKCceXpZKOeBAv0y+jZz7sTUJf0VRVu5TNZYO8poepWJ8lho5L", - "b2+X/kJIsPttHN7FFRoTCaMCVqVg67rWsnqZcJ1x0/lL1N1tmhVzPae9BsaVDbkj7hK4ufkyeMkjTKf5", - "+lRCwuxtj/pRGsDb02NDlHprzk+lZSI9HiF9DkP2I/T29DhbkHqXb9ipVnsodtiaGtm8ze2cMx+ClEMb", - "M1Ln431fq7c0D58TRMetv0+DI6qE0AtIGJeivte1sFFvl+3g5zc34GGzuwREqHZs2n6nuTngAG3+rMJX", - "MxwgJZRobmo/QZdK9haS+EKLXeeHb5BtBonUc6NmzJMC3b2m3KFApzjpSMHj6fOAbcswinfHzzIKnkm9", - "jvB4HHRZlFVZP0djg37DTK+S2OwMaUJXQ1SEO+bZSauuNRsVkv98kBrFU6M3Ow4yOWnWBDQj8+75HuO3", - "yd7uNH1pGFB+xsBR9GHUe/X7XNCq3OP+86p1XElGLJpWOmCrvKQdbYkrBNzpUFr6FBKwu67b3bfd7UkI", - "L0MPf+VGxFb9BmkEgbO6ZmoGK/WzTcNRodWj8+BOu8kVHotZO0pGxU5vNey6NOUgrmGcFqwSJZd3qHbW", - "2yJTDcjtXjKIRzt71MvWdnVQDWpiIpAJ5Jdyq1+k0RThJIkICCUmOWtRIG33Zw+WWi+VHReqoQGD5tjR", - "il+VbjtxpNUqWvZUzUOg+hQQgS9NiFg81mBgIZhPsLQnQEeFvYmORyiAEaEQbCAcReYbrSe1VdAtiSIl", - "8HFIIuxDgECHBtaBlzFFQDmLInXEQUwduzQXd7pARCA910KYS+9cLO5C9+HeIE5/Sb/c3dBvI8PO50u4", - "I58/A/FiN/lGfjHbmIBEW83xOmKPR0iA1GhClNG+uco3QG1kaMxOwBkuRBIRaWdUB94tOthYgh8S/7l8", - "Ptgl42RHr+7KrqVIcVGavt0NB8948tXfe0YmhqY5Z7xZ1rDahVxXtTfYy3vRNAxcc31zKCzfV9kDFgS5", - "Yl2gW2vwkAn/s4/OvgmclbXurmWt5W2Qn8backzCKfCxFXIaR+Zyt85h/qpq4KwNB7C39f47zssu+7r9", - "QmDBv7EB033V777qE5QKyeLFLtEO9DdFzD1hVITdGzjX35SUz44QnjW54cSNtAUW2rkqE/32cw0DBeA1", - "PGS2IHOETxlyECGLgu73ZmUQrrIGMnGnuCR6Vlrfe1W1fGWINj6cA9DnJQPSNkoobRNWmigc4Eur43Nm", - "q1yKA3zOYUwx9adXVk9dLdsxZTrWMhE38wJ7asUNTeP+BHOFBqFDuB3uO73uH+7Xeq2W7Zgyt9dV4as0", - "y82HT0Em0HT2XO4g6VgzzaHgNEmAv9bccznCvc4baGJpliydXj43MdkuTdfwli4R01PvriWTKp3EoFeN", - "S18dh/luw/bZNIYYdNTSkCQXoPrTGqyWiM6pAI6+MO3ngLAVslCcCmmVWko+shKYlZnUnpe1Wo/yrK27", - "DlmMSYuVgfN1LjZqGdJaEmqIQqz1jroxZAMTG0EOG7gUuIHpxbk5C5mQGfup37sYHB4HSaNApEUaLCUn", - "XioBaflGbiAs83QMCg8ZqgqJtABaY80DhFMZApXEx0pCwGNMqJCmfROMUk6RI+x3sJaz+HQ249N8jpEz", - "yb2u1CCWJgeJIsBCIkZz+xiRgK/v1lHR12wiKb3sul21kvV9w4Y0G0+iGVF8DAdu2OzVyFUFKHn7XQXa", - "X0I62E7TMKKD2zuj+AaJW+TZtClxjLYZzQ15zKkpX0Bq8kYkki4ZOqKvzrZx0N6ufl9tvaGlEg6061Id", - "98QeGefZlz3MUHgl1sYKknQ+tWYjutTVVxm9Vy3SNsNjvYAL62NjLuZYZ2OjLEUyxBJRgEDYq43Ymp1t", - "LnjHbIHpaMZ7SmpqugYiKFDWOEbfBKhBZiKyocZO09k2fH50dnh89ra30bu4Pjszvw4+nJ6fHF0dHTbC", - "hUzfvUZTU1vnWo+tmUcsTRsNdjipaATRdD8TgQsZ3ZTW3n1Dh022vTGnMxXBxViqM3hh3qDTizNzi6YJ", - "R7EjxpEmG/U7vw5qUueaW7cmBfGIcd80bepQJTf4jArJMXFN7tou7vPfeS/uFFycLaqSePH8F3/3683d", - "dJeEL43WF7eYL53VTPvtWmwzcEZnLaKOgaglLPb6rQQzKGdaAhoYyfeCsc2ScSbMydOxgS4M+6wuSnS1", - "TDyofbnK7BKO4d/yVn80jSJtTlbupbACfKgB+EMdhp6yAfkSFuERFvI6iRgOLiAmNAD+MCL4F7Aw5zAh", - "cLtwbpEL/VkXMl0gZ8aTNHa3mZE64sXeYGeALWUor77jY5CHMCFV9lFXd1ZYwurd1PT+ki90l23PTnHR", - "wJnr9jhN0pvFyPp82LIF1m1mmqTSYonWvdfO28BfaKwLSa4zMXbfAbQmyTZrVB2TWmwcYtZyMNFHKzbK", - "pdfiFjIoriFztRGWCHP1jIUw+hMskGpdW38ETF8nfaI4SaIpkuwWc3VaU5wywrTvYQFBfuFouo5ITOQm", - "shE81Vda6STMC3NXOlBy9vbGJ+qlEuEoYrdCX1+OUplyQHCXYKqTaGqA4zSSJInAwJWPi4z04TE/LpJY", - "HbgGTVoEBewK0FXGksaJVmE4aPlE5yFGX1/DnTaWEkW3TTMg0C1Eke6LTtEnqu3d85o235CZRDtTi84T", - "+qSOK2iCOWGpQF4qiD6ucMBCbVTzMWslOrFsIsJsd8wlQ0vytZyEC8+WV2g4N9G+MPaiGQ6s/7jz+Sda", - "JrZ8ZOYyWqSxFv6jKH9jwBG6LbjzAYIiQWwZ2ZtN/EfXm0OVurv5Y91AHMaYB5GaOzaqkMi8WaxeDzIT", - "6UvzGLt2Gqw5jVJuBl9V7080zTdxMKDBYYc8XVU3qNaFPJvO59KxkJjLhQGqbk2zUIVOLCAzETZToRFi", - "HphWOm5KpSnQRkcj+YAG7tuGNsvksln8bb5qD6xze60RyszM1F6k+sjQ+EpJyQ0vGgbR6KFdH0GVRT18", - "Q1G8F2d3F/58dvWJVhtbG7uq6xRhJo+fvby64Cm3X8rgFTCOgaqhelPX2c+4EaqdFFDChCBeBJ+oAdFI", - "M5lH4gZy3Rc3kDbF2UDW5XHDOhaV/R+X5tX5QPSGHOIJNM2t3s3d0SzJne2MNHCbjE7ncujiIDkvzW4+", - "NJtpNzCBjJtOA7lDeGPKwdZz0RxLJadp25AzcAsdz4czb8jLCPilU3eLvO6cQFeCUc9mQp4HoudkTHYS", - "KHfIIFx8dl/DpyhG045Qm+i0bsUxVwXkKl9nhpuReKyWcRJhE1CionIdVHU1rsb106fkz5N79ffsfvj3", - "T+lgsAv6r9///Of2fen9p0+iWuXf/60xMk4anzueIpUFu7ydTGuiy4ZFfoXHs2flOKhRecUHoGipkp9y", - "5Qptt6vm/JM1NVbjl+iEuJ5qs2KouC46C6702WtczHQ60st4Me+hGOZ/kt24NwgvotUjqJxsOPO+znT9", - "f2a2Pa96f99GPz179uxn9OzZs/72zvZO0ZQ2+biv8qXsy/kGgR08FWo2UqZxl+bVKJAZxpxRttiUUOMb", - "qHdp3Ri1tV2z6I6ZmLNpa7oTM5CWUjx3o9ZSrub6UtWvka/eo2PjW3W9ebm5geQ0IT6OtJYF0DeS6EpI", - "pH6oBI0/Xu7tDrb/QIzbn/3t54O9P8ohefSL1qA8tm+bZLeuB2+70ZnhXDVbQ9rFbhtG/mQK+JmIbnd/", - "0RS0THyMZrPjsl61NQZFIWTl0S1mudI2+HyReM/3wRtE6eC2NIbm27H6bZ/nMSm7c7dZM9XNvj99yUcT", - "znfol8j/atzU4M43BtKPBcPzCfzy7EuMuXyefDEM8ZaobeE7wnBf3/pKIrsBMMfWRjZ1rgxroSo099yZ", - "+24U5b/4+vyWf3vG5RielSgqv4bPjFJySHLY6iBdhYQH/XPM5dT4zZznur5uq3QUxCP/29dpvMt8Wlul", - "1Q2pgMmRP7YHg1bGlK3ANguEJser+i1/Vsl6ObqqjArvwgFQv0VGtS9za8S8VW77RofaXTCzeKtVQAED", - "47uBRyPwZV5+GxLFyzmgzPciQHgk1UFSHxoEaE8fowcxuvBskreVAPlc/dneU393B0Hh933Y1QOOePLm", - "2ctbfxyyFy8zt3Xd21Gbk+Ql0ABhx3dUH7RxEfwESWYdsmYN5RNtdh1ugPHLi2BnEn7bezkapCUYz5iE", - "Ize4RGGffwa3DrLM04XxteqKm3Cw473Yfsl37gLtXl3ZSqqI2siJyFlmOSnwgky7rXfGd8nORJBpAPyF", - "cbfK8uE30vrbiHlaVrARA3RtIzAYRZS9qdOKFeclEWgXjTlLE61c3kPaft3HAhCOkhDTVKc0Q36IOfYl", - "cIEINfor/dUm2o89Mk5ZKtw6uZBy/Iemjz+2/9ChUP74YJ8Hf2i6t3clJnCrc/jYf31wePTm7btf35+c", - "np3/58Xl1fXH3/7XP/65s7v37JfnL15+/nPvvr/CWh2S619qpLXJStm1eYNuwGD+bwIl4VRo8wTGUcTG", - "+mfECj3+oqdsoQPEVs7Y8z5wAodVzrot19ePc/a9JM0mhwrk19MWU7OOuP2//+f/JYH2AVgGyyVdRn0o", - "lTEoWNvGocPztbtKLEknC098k99Kx5nI56GbR3lZDVSnsAbjp65mLZ8tRGdt1o6azxEJfxN5pNKSZmmm", - "aun3V2OOk/DVZ1eH9Lm5GDVpkhawzpG4hXNcqjeKBrRkRf3286FgMUhtcR+RG0B/HOybY+EBjsiIcUpw", - "5Vh40B6o9VK2WAQKyQHkvvGVabOqVPu/dafZnNGFrmebauzLqCcPtCN4c19W4WpIDU2Aa5sCtbf4OPJT", - "Y4NRdiWvLZ2QjMO3UeozAbknVhMl2VfGHVxtp4ENEqZQTkYIG1dmtZmqJmsuVmVK125Yv+xpwzh2u+L+", - "I514pWP3BnkfDe664nhO84SaCzrg03erR6/2kl8Mx+qTk5XjWQOyALKr5vclzG80U+IMLDaTTvtgHf7u", - "54tqSa/ZYn0eYgnNrCDQ93WSY//GojK7IXFWZ209jgg38VcbwvJpXjiyLkVcyCwmUYAyh8Uud08bvRCL", - "N243dfvpEIsT3KGCkf8zu4LGah9SqU33Lok9XTYEXMZdhqxq5ahbeMzG/lZDkoHbsSszSwv3cqFNjOr9", - "nBChr1OtCVJ2m60J4xYLZC0dFcG0B7Vo0N9HtemoBPowej91El0RSpmd2WVHGmOuVkbWzMIjZlXKmrdc", - "tOOWOX7bg3lGeRuIQ8wmWdC5AiULI0MDk9uglCHSHs7WMM0CVlodlrVKhhKuYz0iQkeEEgnoawopoKDY", - "xZe4ZS4t+/Iab1rQDavXYaFBxvcezEHbfGgspZgQgug4v8Irx/kdwc7eixc7288B9nZh29uBF7v+zqh+", - "zdd0rzfYqLjTZJ023+KJNEm4jjdzxmRhoZidBxotR6z1d5nJl4xqj+mEGKeWJu547wKXdY9K/TcadJAA", - "SqJjky3Dgn62Cu+SQwzdhJpTY/SB8oAHKA9XgQhFxm1f74s+ljBmnHzLZYyxab2QNWzPkZF8NhUCcl95", - "z9wO7e8fHG12l4mMU67o4snxxlS1SAgXGr3W7jx09KpLhAUCIbEXEREuPeqM4R2kko1GXc1VOkrpp/hu", - "dWOO2O1qhpwAJywo6+K11tjhCD9tB389D/7a3gv+2h0EP/9bm759xknh6vjwaKljwrKeHN2PFyulxfz4", - "sZrZ6Xw0WSlxZUeXVYxhzrEmZ7U5JdZW4frPPkVsO8v1nJ1ccbj2A5HdSxwWWJuZj0Sk2uGs6klqOoMg", - "nxEQNowcBLWDT8BZcqxjDxzTK+2kdA7ct740xco1BkyDzWeVBWzK/+df5n/zn28f/Z8/fQo+fdrUf4P/", - "aFzdivEdvD29FvDo3R7Tfapm7Tt0fFRs6N+h9+/U7XeirmP60S7n79T145LYfZnDjAoG0sJiZmW/ehSX", - "56fqsbyMu/ET8r1dOqVT7cJMt1TZuRyjxha6qhg0ViNl6Pt7NkJY9jkRN4XlsnG2YbSwcde3vUqoyKPA", - "14Osqn20y3QVh7NyvpJ539ksJY2WXabzorkKqmbjKI3kzPxzE+B4DFayOI1Zg7vAvqmDbCWj+/EZD4SJ", - "mE4EykWgnP0823zWWVi0ctwppngMOvsFDdQpuCn+6j4KgJNJptWyhiYQa72OQPvbBy4UzxeAwlVXdjs9", - "dfQKrnDBklRySmgqmwP4pmq5KWTHpo4iWCrRLWBuQ4yNY3es+uKvy8GkSSSqJbBQL9SkK65GYpjX+WBz", - "tzumc+mosmt2BIJQ7ViqDyvZEYBjOoaHA1TeSxeDR507VglOsxDXHSarW1k9npaGaOWQLDtdq56qmujZ", - "HRQTuGF90BxC1HgDQ0Yj4Nr4zwN5C0DzWI2VBio8Xt9DZ3fSNmEGSxImiFbLj2yQ3wL8nQWhb5Cmu2Oz", - "0GKsEqF1KXtBiFZFby0xJSoCgZETOklQmcwxw0Oyk5zZKGw03O3UwZx1dFCy77UOktfiIJF5R1zZykjV", - "RseHoquDREOKV3yXOUgMrCVN4TAxR3Ru8pGo9LBIvK7qqDbRhyhAQk4jUGPUVobbg35AxkTaCPQmYhsR", - "JijcyESHDeEOB3BHYh9HSNcWm+gMbitN7f5im/r9+vr4EE32isSOQDdvyQ1JICBYZ3dUT1t5kv1oOjRD", - "HxYeWf/DqsqGe8OfOKYBi3/+uXIQ/X3Qf4n7o89/bg/u/8ofXtz38997HX5v79z/PMvGsYrFzuc+SQr3", - "oyz5IwE+2B4MeubtYKf4uVv83BsMKqkc3c/KlpfAJ8QHdEWa4tlu9CQn4zHw064xPmfGT3PW31Wl3aYV", - "aG6sFdYOQWISNbtztJ+uuybaua72UwdGp2TwU07k9FKtXOvdDdr5/YrdAG06KOQzbysiqWtu9Ih6HwI2", - "QYSNlWHvrp8t1b6t38/qZ8s8Ie9hauylCR0xezKU2JcOJvR1IuPy/8maUwum6CYDSrELrqq7yVNLn9T8", - "z38DDwlLMNpZW0jGQbjJUhH2WCqtvaXYKKKXmrRIMgTC3QCREfGBCiiMLXuvLw/7O/2DCKfaN68M45jI", - "MPU2fRbnnLOv2IHpZsuLmLcVYyGBb50cHxydXR7Vc+ALtH9+bHTa5salt7050IvQwb8eZPeO9U19AhQn", - "pPeqt7s50C0mWIaaULYm20WeeV0ybkqAegEy5VSgyBo44CgqEGgbQNyNgZQJQGIqJMSb6Fgd2SiOENBA", - "60sKeytjJq/jc6Y8YcJkGFMryaZ8spYV+1F0UICqBsFxDOZKs8UmtaiyZQWw+425NU18kQ4VHceyS4n5", - "ot8c0UDnWuJWXaPRr/imXTxWoMImEa5CxdYXa3ZiNukF0/4TTNuDWpVDXdcW2If3htVk8duMqct+FKHS", - "lBjN1++9IiB1Nu+9z+r7Mrlt/Wni/N7bsvn0hxsoUOR+DgRTRNSjCeqN2KiRjixwbxjPYV8/NT10npec", - "Xqs26zifGRtSq9LFTXVaP98vii8b0FlbcLcRAaGTLGjh2hrf+tP8OA7ul+9n/qRnncyGyUbHbgBEb8OK", - "SRe7Yx4SuzjmGGmuoJGqIPHZ+CE3JCbImLEWgnOOLBkyCcYQhdssog3cqdOtPg4bn6K26N+bKPMrQbeM", - "/k268b9RSiWJ3EA5gRFotHidsMSa46mdOI8cbaxg1KIgMpqiG+LfQNBnoxHypsiLSFLfJoxf1hncGko9", - "ymHvrX35dV1oNt+gE7K/M8+cyR85gYnJXZixSD9vsJUHLsz3Ou+N3Rlk7uL2WFvu/C8g5JmH3yMw7lk8", - "eqO3t8L+8sRlDb29xkHu76673X2Ubt8w7pEgAG0W/eyRxprzP3XIBI5MdrT2DbFpC1RiOWeRycZ0dXx4", - "dP7hw8lw//D0+Ky30Ts4OT47Pqg+mv+O98/MDtrIm43VMsIOD66tYFPnIHtpHctfs2C6HqZ2/0jcc6PU", - "zl0clZupHn1nr5gu5D2fFhcnHDuB+ezMpJwqhzfccOizALb+zDnj/XzGn6fVMdjJ0hoVTr81InoLdhd4", - "Pb3MWfCT2SbfQrb61G5fdsOdL402yE+iNMY2EWq2ZuZzZbL+zEzd7s3MRNDoZ6HLi2OKzUnFQQs/lBXR", - "7fKgfVakEugnDwQJIIsfa4t/rks+phOHJZRmca8hMQRDB3Zay5g3Lc2gX501shMtelOTUKGF8L4ntW30", - "IkJvMomqX9aBlOEt3ousXuB+UKa+PGye+vTfLGve/JoCn27mbwxP/c6Mqlhia9rfFpIsc6tRfWxJG51J", - "tCcPpgjurB6/ZX80NR9tf1x027r/nlz2e5GdVVJrUiirp3//rKa8oEs7zyshzdr+WrDsjnrPtnNVlpZu", - "E804YC2lqRSAuR+u9pDV+dxmVPUdKqoJeCS11jJKrBaF5EP50mx1iqtJ8etyvFGmtYvy5v36uBXBdLZA", - "Pwe9Jcm2TTPYZb1lpTr+60zZ6UI7KZZVvCPO4rxgpiCUIXThkZZkoHYd6DwxKNdDZzxjTCbGnITwLKle", - "u3jUDv1aaGIBkXxVWuFibW10rGsiBs8TESryQdO6c0WEp77uShvi4usOQr4lptRfXOHdgfftU0Rq7M9Y", - "nuoU/0kqQut5LUHI/ISTuUXr23kh9TUODXSEsSxQmlY3u+kSbkNmo3BThH1JJoBE6uXQmDBX2Jcpjkw8", - "biJ0/zoDx5T6IWeUpSKabqJ9JFLfByFGaYSyKUEx4Cz9hurC+QZJLG50AmEPgDrR3hSQpcDupVx79hje", - "DO4n+on+pnDE9e0W2hvsaS/PNyylQZa4OFOXZ8HkKuM/eneBigiD9WPh5ZT6R+8urMtzhfZ2GqbT9yGR", - "EFQoUDWj+7INzdJZK+ksI4kiFlQLbWbXMmVZbGkqXaSjB10FLcK52i6DusNXlM7aKxvFEFNZUYxpLXA2", - "0tx/vWkHPTbVV7OR2saW21Abx8WzXbY2rM2mHbXjaL7fxtoVQ49CptpAxA87TkWewaIkpakXWsapz8d+", - "9kE+2Ct2LbTF0To2YdzS3aMptxcghRw1BSEoTFrsLLLrZwwlcYL9rZGvZt08La5aZHcfVrPDd7Qz0Umg", - "mxPQG0FBSzkeIJjgKDVpXEKglvZ11vvi9KK3/A80ym7PEQ5i9d43t+gghLEnz1ZXsx3UaWPu/XVytPZs", - "/51P5TNS5q/4iJ52lVIls6GHKhqd6jy3nB1mTMPqedjMGbh/slNvTy7dJ39pQTIGPoa1nHBOVcvqADAW", - "G/lZxLUf3bAyiDnBFDdueRJ5m+SoRWGh21+rrjouenjQOVRD2q6XXX7uFrGDcqJLrMASylp762kilEiC", - "o1LO//JU2drHpqJrIr6OaWuycF/3aq/0NXeBZ/hrwt0Chk11api5TetdsKjWuEm6r9ePr84cMdcO5gCi", - "S4llOtcSbOPR14aBEFzXfWHcb0omgIX9X521mRZOdPVcoD0vjOzXwu3mrpedR1svLeqUDLMGMY6o76Dm", - "wasntzNtXUbq3Ikde0tvOkspXWZ3T4UBqTGcdmU6ywqWWSjrrnbIi52frRCqJQXNCswia9+GrPy55u0n", - "79909xR3n0y8fPimY/nbUHOymQummi23tk7OnUyr61wq1ZTiXVdLLd3vKs9fHTG8xWHEQYSPdyWhVYa6", - "T3c/s7k+s0SlNu5Jrk2sn78vTBsd5niuHGCAmTcbSwvVGb6zYDQrR3PJUDXDp9RZDJuuuM/zgDdr4lfl", - "/JVrZlW1zpoy4lK4jaZ9a35eQlHjvXolE+QCuj53rrf+dEMLdbxcd2ArX68b30g8HhcDEC0XBpUJ7mB/", - "6M+yP5yHjXWqwl0Mdr9snrUGTN3/hmsgC+7dTv0WjWuhfp0DZb166DqtNHNLRifAZWWt6UsSnUWozjXN", - "BwXJXDGbpGVtE5qlM5rFztSiVXBBYOHWxoIvGwaMKWUyq25CqJRGjK5CItQGHAWI+X7KzV26CQ1glOha", - "+W6joJrwH0o0FlrXFmKBcMQBB1NzJ88B+yEEm1XmagFwE89KhrKEN4vTmehijJ9dH5TPzS0awBMipHPy", - "+xeyFhTMumA1RzKX00QHd1fVkDfN4gtoY2jHMp9xk8zN1VIUISJMOC03OoY/jv/yxvHPjTEYaxo+EgM6", - "NwF6JMvyTm/YUK0mY7weRjNwefi2JtAWD6pch+84CxbeAkCEhQ1SCGUwusXDbwgqXA0OnIUSbgHAH8eb", - "Mb4r9T2Li7yJGJYmem4jAIQuAwChqwIgi9lXBqIcuW8uPA3BAVcEXhZKMM5D/qE85p8D5QzYZsQMXBGM", - "DVGdfh9sDvrbm4PPSBUdvD1FJkpGG5D1UHuPApsHEbtFz/ZQPN4KTubAVwtv9YggPh90BLEcmu9RICTU", - "hOpCz/b6neH8bkA+H/S3X3SFshyu7nEB3X4x6O886wqpGwjuUeDEHpsA6g5gNVrdIwK52xnIhkiSjwhn", - "d7qsRARd3V6sw6cuyLGzyKxdoTimHWBYjjOvEZQFOfBaIFmW064XmCU46noBWpxzrgWeZTnkGoFZihOu", - "EZ6FOd5KYbmwEv6C/O4iF7lXCMNy/G6NoCzI79YCybL8br3ALMHv1gvQ4vxuLfAsy+/WCMzC/GWlsGSq", - "hUyTkABHAZ7O1yMcYhJNFwVljnB3xaTOUZTTTq7KakWM+mKlCDEwhCzlwrhCGbesDrDob34jMrQeRysD", - "JsDTRWFRn6wWlEuJaYB5gAKYkDyucUkj1U0PJWxDh1k7q6KeAwajEfG1yvzDCH3E/EFw+kVzH0Z5Yys7", - "xyys1PS+t1LTe2JKTW+tSs0VKOW8p6+U8/5FlHLev4RSzvuXUcp5T10p5/0rKOW8fxGlnLdmpdxCB0Tv", - "6RwQvSd2QPSe1AHRe3IHRO+pHBC9p3RA9J7QAdF7SgdEby0HRJ2DSfHipe0STBanFaGkAGdZK4X1gIMb", - "xfvlDBVWCuE6rRXWg0q96S9norBGgB5kl7B2uJYzRlgjWA+0QHgMyJY3O3gM6Ja2NVgjcA80MFg7ZA+x", - "Klg7cMuaEqwHsPhBhgWLwdRNvohXYWawfsCWMzpYJ1wPNEF4FNCWN0h4FPCWNk9YJ3QPNFZYP2gPMV1Y", - "P3TLGjKsA7LsCOdbH8LOFg3rBOZB9g3rB2w5a4d1wvVA24dHAW15S4hHAW9pu4h1QvdAK4n1g7aszcQ6", - "IMOrsKBYk7Dtqss6WlGsA0WyyaaiqyXF+gAq21V0tKZYCzgmjsaa7CvWRFshIMdEYqVGFiuFeJ63mgIj", - "wkKqyX3DWbwCj7Wju+5dXrEVdPgAnbT3tHTS3jp10opmG/XSyxqbfG/1qvdE1aveU1avek9Xveo9bfWq", - "9yTVq96TVa96T1m96j2qepWvwkTku5+xvSd9xvae8Bnbe+JnbO9pnrG9p3vG9p7sGdtbxRl7kYOkAWum", - "MtNb3zF73gHHe/wDjrfqA84Bi2PcF5Bgk5y9EqxGx+k5PhS9jR7cJRELoPdqhCMBzeDpEDouUERCLErQ", - "/e/fcX806L/8/OfO3n1DVJS8AHOOp+pZyKmOsKKa6HUfgQ1aJoiEBUagAwo99hB+C0GGwEvZjYQ6SU19", - "iIlvuK1AAiKT2wcRgf6dMvnvn6g6ee0f7hdaDlv3J9gcbyIsUCogQN5UH9Gujg+PbEqlnz9REep4Sx4g", - "FhMpIdj8RFvITlU4YzRzVbnQffQaIgl7jEWAqQklvO5oY+LCdrBQ9oEZcXXXkhEwj43op0KygOAoi5Js", - "slplsz47VuK+7+cBJtcWK+7xAsV1nbFKOMQCC8sE69rCQpAxHUo8boiM+N3Dwu1r6BqjwqWeAJeptYZf", - "NG24geIMgtYcZ1uSABLGomsd9bgtAPq+Yof1UZhA08eHQg1WaiHWRGHFY2RmLFYU01suUYHFqhvy7Ypl", - "6cQfHmM7py0TWPOp0pYNpNkU3TOKkGZD3YmsGuNTyT5PiMzOsRA6LV5GbtmYyyTmjtjJrpeNehN9iIlE", - "dhjIY8HU/TiKah8sSaD1GKdIIXQNJJrRZccc9RnaSkFgN3VmaMJBaKRl6JFMyRExDkDJGriU0wjZiMp+", - "yjlQGU0RTmUIVCoigCCLM6/aiLH0y0GZSbA5m/5WkuC+aGuZ1K5OusfFcru2DuG77bdOUPDvFWl3wfTK", - "aqfQYZUJHTtzYYjWSQ+fmrRpM0QsterWmHrOd/t68pKWxt9RhrzWnHMLhkOeFfm0FBb5v5ucWw58vKx8", - "6zD4LZsJduvPhLMJCfLsH4+yfjtUzqGasdovgQY2qLyz0bjbg2RZyluETRCHrOGmIMqq3nnx/gH7hm0M", - "Oa2tcHfeSoDHRIgs8dGj8dwZa9kByWZFzja7EAvEJuAcZYs0QMcjk7bZ+RhzJTZM2I1OxYww4iDSKBfD", - "TJDwjdIsJ5yNSAR5fkWug9UHhkkVsluRUjFXpPiYIj/UymrdYAHH5mwGdO7MwFp5kdPR47GleqcLcihU", - "xs+DuZUzMVt/Fg8dUhboEOJ0HLmT+1+UQEtybzEBq5SAUanZ7yb/bTSmNEvKQ25LagY0jRXUmb5N1Z4Q", - "uO1t9NIkYliJ4pRJ6H2u68c/L0i3XEfjFrPJNA96bz/8m0ARFupsaUJ5I5Pzye5sqpSlAgmQsyngwva9", - "fnZhe5rBKjZ6ey1UZwZZpB+flySAg82NEJDRCLhOzJPlRPubsM3NpuECMd/1ADNjM51LDJi6ydmPD2dv", - "Vk+JEmZtGrMmZsGFZ1bykENMqJK/HlNKapVVBTJgoQysirham0X10bX+5CIbyFI6JC0om4aQ09IiWDbX", - "QmKNyXPfAlVj10mGRMIBByIEkPZCSh/kVefqvChvmSV/0aA3Mc3oDLQX+ts1SWjjek8PypmbQZ6p9kwO", - "3XwIi8yWAD4hPgytHLOerF77QSBcXZ2SrPzIMi0LQX6XpSUldVIr2JbJF92Q8z8ILs3X673VwtV+HjR7", - "+0GAbHMz7qGWztEmQEpCx2ILQj43J/3RuwvEIdLq0+zDJgXj0buLy+L12jYHCHnWzSKKRjUKB7wFUbn6", - "zPB2t2pCrr2qnam6qiJ79QRdx/NypOwMdHn8dyDlmNP5CZJOL85m0vDpxdlj0HDM6TI0rKB/gjRcAasx", - "Z2wFr2vIGltD6YPIdQFUdyHOJdO8zqRVNxfoYxBt0tDf0vlfZyH3UYm3FaoZR6A6ytemsWvC9oMIu/Ms", - "dCVxbcO21jyvNmVe021anplw9ROgBqY7mZXR/XFzIOYpXR1zw+abNbxMnkE9lVt/qv863dq3TY1525w0", - "comErDPGsk4Fi0HDXP7RggPzds3k+f3JMsuy2k6QVTQtT5AP1xYsNumNTMkco00m0Q1EAnVa1Ym7CS3Z", - "qHgsmG4gQq1ORhsG12tfX5zUT6y6izVTzuvpcfD9qUdP6CziMdiWtwxlptILkU+aJByEgGBImUK8GcN6", - "dqsjJ+G7WRX5uHIwUBmMFp6RVz+r1F4HMczu86HCRtEuqg5mkYmUJICh0dx1yYFrdXxuFlwdH0B1ibyp", - "sUz3sYQx4wTq83BFgkLXVyGULjle52V0nXGbtNIMr2txt2gHPvddmEVvzPsCvrTMZ6MXE3psPtte2JEh", - "95hBMaE6VTMbmQsNNjLXHNpGw1oUZjYyM7LdHmKJD1LJRqOZ41yt74vjJuHQpKImrvVvaqtQsKpdg/EA", - "uL5Gtk4UiHEEcSKn5oY4gBFOI1klcOemWLUIAeojAZClvDaeGDnp/d5ryXbQnGGgLbx7S6TygLPkmF41", - "htdvSs0aA6gDiqmqj4ZdaLW0uBsoNLu1XWKkjTHiV4yEZVEz2zVInarHrK/K+uKGJH2maRNHfb1xAc9I", - "/a6vyEvTVbnoG3CWn8fnrc4R0p8qwqQI7vwoDQBtacqt8GXK8pQ4FcehttVqmztjtcw4Fdeg5ce8Xqci", - "qbeYxRyKPhK4NciZcYvzAD3JrP33YZeeC1jY1qUnewuiYWiWmK7I2uxonQ4eKgtZKLuoWbok/NdXM9qa", - "KFtLkqHbkFiT9uJiLMQCYd8HoWrUMPiG0MCx+q/McNPaizltWnHuztfoTEq4Dr0E8z6uHxPycxPPzKJu", - "Gb8RCfbbPFfz98fB4t2pSrWOHCDm93mlumm0EsqWxEYPQn7JUq5B/NwoP8xZXmw0EiC7HGojEhPZewwn", - "SUPUFxAZMT8kSWcNsSLE2e6SlcVRstCAkG+JKfUX51JdzC32lRRW5VRmrWnRKkmFWXURlurwnwnYdnRI", - "SCyJkMQX2tbm/PCN3d/0olWL+OjdBQKKvSg/DptbwGJBZ23q5UwR9qWSfEXq5YBuIkW92JepjoqmNlSh", - "QdOmhVPqh5xRlopouon2kUg1TxilEcqoAsWAc4NbWvoGSSxudN8eAEVq2oM00l68n+g+2hvsFa3UpE0y", - "Ult8A8TGItEDNGIpDfSAjTOLI6xXbFim1D96d6Gd4BlvdWnZaZhD34dEQlA1ZplSX2P/UJ8VGJ9hFt/t", - "jrKRPvMO10Gexy20aTZS4zuVkSKh7qaRh2IwHxQeUdaPaI7d8qUd1VpvQGwni959tBoJSvRGUVvjJl0b", - "1yK79dafqXYVvM9EqJn3fIVTn0DY4luU/LtquD8hwnJYUSL+xSjqv9i+od3sDxyWuUqPMjOhhWxcn2rF", - "Z4dCb+RL6BXz9lvuGFrkYwG82GQ0pzcQ5P4oKACJSSSy5W6cA7EQzCeuvt4u/znLXLHGSzvE9Sz1oOhh", - "zevc3o8xnh0zcAmB1SAJXdY/h4Dd6blvEx5K/oOE+iwmdIwu1HcoBiHwuEELec6Z2qCP3l2cmioPwL2V", - "Lo3ybfmTjIFY7ZaZtttBUY6YDVOvtzHzQt9F3pbeeGaisC5/6Vgj2o5TfawdMqk1pzJXMCYqyRncfuAB", - "8J9NdIPMV5gGuZChRJjjwlfQ6orDfDUF+WryjdV4JrAYpVt2QNVij9XZ/aEEopwE/9Dd6ffqcyy1nqFw", - "QXZPcE39aggPU221H2H/Rh1OUkq+pkDVqc5nVEiOiWqBGRXK6cWZ7vPww2s0IhAFAhH5N4ESJgTxIjAy", - "XpxGkiQR1KQBxzM6AwVLyYmXShCbaF8PVKSRbFAt5vdgVhpUYOi+9akUR5GaKYszkVUjXkTk1NjtS+Ax", - "oYBCpg35Q0yDCFCQGvrW3ximlc+bwYWFmgh3crKR5TTicyKBE5wDjgM1vIqnnw0FpahrlMqUa4HIEpSS", - "qFVLmm0winAuDf9sNKmV2zzVs1kI+/lRe30me7q7C9P2I1zwFT3O1WUtyKh1s5nxMi7OhkvL5YbTTICT", - "0bSd1TT7fBvG54c4ioCOAelWLMZqM/5Rd+FwyKU5rWlpdczWtE/0nbrpqiKtttl7tJ0wAhu9YYkTRl59", - "XswF95DRTSBo9UnTrm+NRiaLCf6LipE5sgtJUkl+zSeG7nOgmXg2BZhOzSkik/L0+VrvZdkxQ4Yw1Q6H", - "zilDiTvGdB6xUcZSixNg+URS7JG1jc+A4TLRfi5jFlJ9UweMam1brAagQ3jo7E3qqUNfuZqza2/qN6PR", - "NIsWMmNMrz7RfmNflqY3UAR4oj1IioguCuEsNfoT1YPTBtb7P+0XobmKNeIcfMohYtT3NwCJOS9mTobl", - "LzbUS3abBZ7QcoQfYRLXo4BZisAUQYxJ1NJ6XlnxXb3PwQQoIiNDQj6m//f//H9aDtLdQIBuQxOChAMi", - "wrzN+lA7LFdyinP8yEUsnPs2NHGAawG8iPcjlmWl2nNXtWXugovWVrrI3bPckpcdCveZzIeOjSBl5Q+i", - "ZJipQjncSaBWZhFpoi+O8+BWRuPTdp4z41SYODS9rEkOMWA4/bRKIos4D9t7FD2RBfxdTmh3gCMZbrnW", - "N64EUEbV/9KVXXOV5ejObQH9Bl7I2M3SwosdQcJB2wC0yy9XIaBzW6sgNKtmJdQ4muvVipGv9oJcZWCP", - "JZp7JBH2lehuLA4Ub05xFE31urUC/tG7i010aZS5nmI6qg8lJee9v2E8Nq1xENoPPgiIuX1FhBorCu0/", - "zzbUPsTBBzJRQCapCQmzUYPRgxHjDmB2XBrcYLPatXqLI6GjRZE4iXSWVwiQYAhngE00YFl7mj94oKPI", - "6DYRUEk4RFO9l4RSJuLV1pbANPDY3aaZlU3CtnCSbOGE9APmi/8h8XjrkIyJxFH/AHPYSrAMRT55W3rm", - "NhrJLhvBciRXGv/qaI6NOY41yaWt6+UtyHNT8ZpHvSU9FnWIE9UGMo2sAm7RGXDxYLDFQ2E2t0Jbt+Bt", - "aQdtvhnKOGrVI2uLgOI2yWqvSlcoM6Mgqe/PD9+02bo1WtYUN5jtFlIdr4SLu40VNMZB1fAlBEPJboAu", - "1ObnpWY+R3+rPca8yVfNgZ9yIqca4wJ0xIkrPYBXv39WgCmRtNn6ULU25tkWlfKo96qXsSglJaieNp1K", - "m1lIw03Gxw0mOwlnQeo3NocTMu/rACbbte9U4WYAk3kff8X1b79i/SlELNExOec2sdPQxM6MJj7nE1az", - "8tZJwbOD04b5galw9YZayrLEl813HaqsJUZHxG541r/TujL71sVkA4kQK3JUuzSRIDYQSN/tw22ioaf9", - "82Oh1aRaODSaZitwqm3Zm6KrbPRFozl51ts7T72I+LkMIXLpwZsafYjTjNFB3H++//8DAAD//xt4ITRa", - "+wEA", + "7L3rctu40ij6KijtVbUm37Js+ZLJ5dSu7zi2k3hiO/58yey1xjkakGyJiEmAAUDZykyq9kOcP+f19pOc", + "woUkeJMoWXK89p4fcUQQBBqNRqO70d34o+ezOGEUqBS913/0EsxxDBK4fvIjQol/HKjfhPZe9xIsw95G", + "j+IYeq+L1xs9Dl9TwiHovZY8hY2e8EOIsWlRSuDq4//nN9wfDfqvPv+xs/f9b72NnpwmqhkhOaHj3vfv", + "G3mLm5Nt9W0AwuckkYSp7w/0S3R82NtYGpq/cRj1Xvf+21Yx7C3zVmy5nRfAEEznIMDU6IiDn34b9F/h", + "/ujzH9uD73/mDy+/9/Pfex1+b+98f9aCQg5YQnBFYjiiQR2LFyBTThEHn/FAIFsdeTBiHJAMAY3JBCgK", + "sAT0E9z7USrIBJ5lSP+aAp86KCh35456xHiMZe91TzXVlySGeQBfSsxlZ5DxSAKvQUxod4hNf0vADCE/", + "otiLoBnBnMAEkKEOge6IDBGY6ujo/QUiVMKYY12/GUanfRc6C4jHWASYGkhiTKKcPKvN6JeN48te1cdG", + "6IRIaKX5/PUsgo8JPQE6lmHv9XZTHxGJiWyD2rx0mwtghNNI9l5vDzZU2yROY7dljVDgumk2Gglobdu+", + "rcBq2hs0tpdgSYDKVnwU758KB7AQXeHxPKBNlZVx74SzCQmAHzesinP7rpV9Ox8vy8CLJjIWzlkEbaSg", + "3zWsLmdAAjD3w/pg3qZRhCTcS2RqoKzppn5sI3N6CjGHAxa0QltUmNPQrLUr5q/cWfhl3hfwZYFdwZq4", + "9SXjEjGu5hrTAGEpOfFSvZlsjjfRPxQsiHHUVz/amLRuupkQf/rP1/0/b27+8eyn/3z9G+5/2+//6/Of", + "w2f/aCRJkcYx5tN2jOTvl0VK3kKGlVRkK6ChP/ty2c4kCSBhLFLNkKD3XQ/RvFOfvo0Ylm9JJIGrR7jH", + "caIWQO8mHQx24b8/33ze2yjxIvviT/O/+c+3j/6z3/7R//yfP/10cxP846ebm82bm+A/nv3nsz/t7388", + "a2JAG71jOguGF4tDoPpq7Ok6Udv0ZZokHISA4IxJMiK+3lmNOMtZAlwS0E+iveLsGW78LOcx+Uz+1trD", + "542eJFKjYTbM+SDNWisGeUUMPssjkrZ0Ns0Ab4JVf1sDTPfTAAUOgkvgE+LDvu+zlEoropfh8SO1rQxJ", + "ME8I2Ojd94VkSUTGodmsg97r3u14NMU4iUdfVHtWBlcNCvC52dQXb/Tll4AJOpCR9zV5rhs1a3GZtnZ3", + "d3fH4tv06+79i1dm+bko1Q1vOFioDsBB934VoTWst4Dgv7hlLweDOAp2zaRiIZhPsISDTB25YtfCTHpt", + "hgruVGeW7lBsPRfgrBuU99NEKN44PgdOWLMOd2m4JWIjhJFIwFe0j968O0VK1kaJ/hL95KecA5XR9DXa", + "DjbQi2ADbe8FG2h3EKgdozwkPAGOx3CISTS9MDpCveN9UwkFqhbigANCx2q95Qxqd/N5Phyaxp5eB/f9", + "MevbwpHirj/v6dVQ7/IQIonr/R6S0Qg4UB+QB/IOgGqNpaEBRNQrIjIkqM1zAlwQRs0rQCxJmNrDUS7G", + "5tDvLA79uyj1mYDTmEXt+LKV1ITxRjhdIJ4/CIilMJjB14A8jTEZArfoyrC5LNp8BqMR8dV6/jj6hDkx", + "Gpy7yT28uRwJS7UZ4Kn4lcjwEFcbmd0EobL6eQMgndoIsdhvXpBV7dWtW6HFxqpKYz+m+3T6nozDc+A+", + "UNmt8jwg8son7K5zwyfsrlu7R/eSQwzdoXY+6NZD96a7t9kZFZ3xcIX5GGS3Rk3dbu1+Ar4ASWS1u7fd", + "GRO28tyWmcTRzEoxvi+tvJedGUCM7+sLd4HPSZmf7S7yZb3j7p8LiWmAeXAIkwey1VpLD+OosoXpVKwb", + "5oXaMNnIiDIiASrVtrRPpygk4xCN7VbFMR2DuwkNNncfBtBiO2dTCw8TPgabO8vB3yquHSiJWO/crtgx", + "H5Xbg06bVBMQD0DiSiS454tBXuZKi5FjxO5WTY0FOEvisWjgB9BimWt3JcWZaFyUEgsQlkfgo9Nhs2jT", + "nRjBfL96/lgHbBms1lt5bNpsFgW7EGg31C5EpXVgHojUR6fXpQl15QT6QMr8gSS5BC2ukAYfSHw/hOqW", + "3apXvU0/bI/+cRv04rvz6nbmh23LP2JPrqnZ3QlO6k9XSnMlaJZBYamBx6a8mhmiC/HNw+JC9FeCYHn8", + "PToVNhhlutPhBPgadOYKSMsgs9LEY5Njg+2qC0F2QOdCNFkB4yGI/CF0uex+rPG46k25DNCyuPxxm3Pd", + "6NmZJFe3S5eBeAASH5MeK2bgeWhbBjlOFwsixfnyYcjoBGrl8DkmtOeYlI1B3DFut50ctR42tJ4stByi", + "tJyYzDh0mHHCMPsoaPa5T/vBV/spV/MhTvOJTftRR/u5RuuRWev5WP3Yo/WcsHKG2XQm6fgkeOM4yZ0N", + "ZjkjCOuNgIOAKMrH0XnJh2CWA03Jo+H7RvWgHMU4QZIhwH6IRJokjEsItE+D9Q0rTsprcIsmwJ0F0he3", + "JOmzxMDcTxih2rFK8hTM+C4llqLR12I/7197W6QCuFDMRG0SCrqM9U5wlIKouVX4jI7IuKP724GunCEI", + "SxAdPzxUde13xm2ou7PdRs8isfMM5k5bzkSIDIEN9OMv4cxy8Jczy1/OLH85s/xv4cxiWeQppngMMVB5", + "TAPiY8l4E7sNgJMJBIahoruQ+CGCOI0Ui0P72wfuXL1YABHtUCxGZe3tLEtxi4xifY5B72bOUqvDxsG7", + "02sBp4SmdruaU7OLE4ip2dnfSIltXQBYv9tTZyjW7SLVDZDH8anqBsuavK+6db4uN61uva/bpasbFI/g", + "ANYdkMf3FgtZyh+yUZa+X3an/Mtl7Um5rNX2tlbrUmzqWCvnHWBO6Bhh5I/jkmw56G6PK3W+uDGu9Plj", + "WYZrW3xHs3ArwhY2BZcgWBZrP8QIXBN6Hkps24vS2tKG39Lnj+q/V9neOqKskxPkImu1CssDvCAfecX+", + "5ZX7l1fuU/TKXXpRzzwOW3hNF5As70/6I1b0X37Nf/k1PyW/5qWWczfn28XWdB2mB3rfPvrq/stb/C9v", + "8X83b/Gllv8ql/0D1/vKFvrO4C8/+7/87J+6n/1Sy3WFgvfDpO4fIXL/FZzwV3DCYwcnLLVK57nVL7ZQ", + "S4As71f/+FvrX+Edf4V3PJ3wjqVWcod4hMUWcwWahwQkPPr++1egzF+BMk84UGb5Bb5CqboMywPCO37I", + "6v4r3OivcKO/wo2ecLjRjGCiGa6l3YONMh76wCikhmaWD05qaGwVcUsNzc6OamqNUGpoacm4pZahLhXR", + "1NBWU6hTt4Ct5sYaEVV2T2pxYW7xQl4o+qru1dbqn9YaRDA7HKDqsNjogNghDGxjbpigEyjmzwkU81cR", + "KOYvFSh2MCtQzF9RoJi/bKDYwf9RgWJ+a6CYPydQjMhp86U1RE4RxTFsurtU7xxHDO1HkhkCdtNQ2w7V", + "h005z82tIjMuyKmHlAUBBzEfz5IDyH1T2aLNx/SUjDmWOl82Bxx8pNE0y1lfd0VWaJiLY4uqfCyX5Jtu", + "Hmgaq+140N/Ze9Xb6O08H/T3XqlfzweD/iv9a3swGPxDL+xmvFzp4qKt7EqIYcKxL4mvZL4QcCRDH3MY", + "iqmQEPc2ehOQwAnFfDr0Myzq4Bedqt9c4WDSpc+82kRxvpRKPh8JplqGh+JKmtZLaFqQX/Q8f1GULjja", + "6EVYKAUAJATn+X0gc+/YyGvaVrJk6rM+0vRvF2L+vSZIIiEWC/dpx445x1PdaMgonGlxb35jTt0MJiYk", + "jrLbN2Z+nNfMPuUwAs4heDO+psTcoZXRXjzeCk7U+o5ZtHXSSLSlaz9mLs6sou1XSb/dEahqN6Gu+/6h", + "eN98IFWlDMDV37WwkV93UPBRVTLYHgxqbHTuclFfHrrs0232LQdYpskYvjEK3dbhla1tx5bq6xceyAPu", + "wFNzXWog5aTxahRXA9JXFdjbC9ybZhz+X+ZSZXjr2LRz1bBAHKEs36/q22mHe+BIAFSRCPDy1rozgp29", + "ly93tl8A7O3CtrcDL3f9nZGRDLP53NkrTa96nHHXUQ3xpRGYO43adiRnmh3msD/ixMdb+x4JvuiLFLIC", + "3+fYeQwCIob7HvbcwmhMFKcrCkSMS1+JGNznNzjGt8x9puOUlJ6/pJHzTITAqfMcYSqnHJwSjr99wxMS", + "RW5h+iWNvdTt+QATztxHgb0IU9+tAql0HxnFt/oqIFtwiG8xdx/5EMTwEkcYx07xF+KxVDqDOmQpjpyG", + "j6LhPiapg2u1yiW7c0reYY9xtYLzkveYY3fgv7AQUwrCS/nYKU3d+fmA46TU9YcQc8lSB9wPZIwj4j5T", + "EWLhfHOCx8yZ4hPicajg+4TF7lOqtDT32UtjD4uQuGUC3zp1TnGEPeY+J6ksPQvgDiGcKkJ00XPKxjgg", + "InTrMKqELqeXM0UEngPGWfAFx0DdKgTH4Ez6GUvxrR8yKYuyjyke44ClY+b0ds64ZP0zNnGgvsRseFXC", + "zRWJvfRWOt9dcZIwdwauUkocfP9KaBAyuFUlMdi1iEuP1A+Z0qBLZeOURGbq8yJJxmmphONxigktl40V", + "N6NqEQFlYrhPuFa16hUOsMQx5n7z5wcsZsEFmeAA2zloqMID5jW/+yX9kk4b35zg4QVhX5o/OwUasG/N", + "7y4IG77DUQSWnmsVLrHR3pve0OEvqWGPjS9PUtLc5lXqp3HLh9ciTHEFN2kZHyKlvtnG8iJJbtltuUV5", + "6370Boek9jx8g2kAHIvSC+7hoISMNxBpNcR5VkqRU6CYZv8Se1EJqjcMDz8RUULfGzZmlQIiSm01U9gB", + "jj1OgjEM3+BpuTxhw3dcDaRUTP2Ulgo49nG5xTqlHuApUFpuaFqeqYOQ+HjMyiVhisPSKjogaYADRR4c", + "vrnljONo+B5zj6W8XF6h+gMlzA8vSBk+DkKWcHyQElz+LlUDdeE7xDTG/FaEeEJLxXeC1QuGBxxKjOUQ", + "6AR4qUBypu/AzEtYrIQJF4yjIGa0DOoR4SmFxMXuUaS2ygkOmNvBERVAceA295ZxOTyDqAyxLv0VT0sz", + "pgpxBKX1/i7CfpVy3rFAhtgrlTBRq6Uoa3iV8ttSYRW+dykOIGJpaXTvUiwhxlGl4hR/TfXtpkXZFJf4", + "7XsckZG2FhYlk0oV4DETJIrcmT6mAcE0/19tIaLh9QfK7huKTzEHOm5q71zfuZwJFZWXVxBFQ2sPqr77", + "BBPcWE6orxZaE3S/Eopj7Nff1IeTTog7LcdfcZSWCPMXHOMyXVa3kF9SCkaYtAUfgMrUv51unbCUiFym", + "qb49ZVQSH8r4V4gdHp+5JRxHQAPyxYXzBA/PscsVTkjswnii+B8dQ1TCTyM8J+wO+PCcK3y6lU+xD4SV", + "Cigub/SqJC1/w8mYyXKJJJR8TaFUKHHMOCt/+g3LqMQn65vuKVDFJ6DUGHASlCvJCN+qxkqF98RnVSI7", + "VYCVd5xTRn1ZLZFKw5tWyyYkAFYp5ICjSpEAzrGLkzOcaR9ZAdwN/8lK/OGMJGRcAuPMCnz5I2c0xOUS", + "GQ4P8S2TaoNNIxy2vT0AbfxoeavAucTlDfssTV3wPn4hFI/d3s+xWnPlgjElXKZ0XCrlassknou485AB", + "JS5DUVJvH6d9Q5aVF0M2Gl4mmNBKORvu+xxqhZ8gCku9paCKL4hfLqUSD/cVW3bJ8gITOh1ekPL+dYHp", + "LaHDYxqBO7EX4JMRlArGZTH4AgSLUlmqQ9jwDce0BM0FE5iXVt8lVvAdC+xBVC3mpalSRaQsX6giNtR7", + "bKWcDc9xWuJAlz7jILypSPV153lxSBLOfJcILklZQLyUwzeYy1DJetNy+S8spKJc9IFIWSk6SX1SafAq", + "ZDGuVDOs30X85R0ZyeGByUPnlF/BOPWVJpq4zV6FaYkDXoWpkmEr2/YV+ZKWN8wrteQkK5dIVuIzn9RE", + "pmVq+UT4uESsv4ZEQsh4SWr9lVBKEnAXyz/xbSpLrOOfaru4u6WWzNTc+9IaHgyDKooO8cRsdk5RqkSq", + "w2uebwLFu1Psf00xJ7XiTMZzyvzTlAesXHiOo9gMuyi70EcMuFx4yVIZDs9ZFYDLKburVL3iLIrKRZ+Y", + "kExToS7YOmF0PAXMvSloKAVRiqzzO4qx4fn6KbaiuH6gOJjy/OmrNEzZPjAP8icRjrFnJsI+34bYw0Fe", + "IKe8+PgNHodB8fINDrllVubx1qlJx7dmNOaRU2zsVPoRCE/zTt8QEd5CUVdJwiR7OsCRn0qjFennkLgP", + "jHg4EsXID0JGx1/NNbC2IKXjW7eARSw2TFo9HmLfx8VDjIVvNn79HFqbi34gUQ7VYeph50GEmBZIfYtj", + "PE5FAeY7/C3/rdSbAmXvweOseGLDg5AMTwkNiyI6Hn5gBfjv2STH/zG/TaXIEXcsJKZegeVf8C3mBRS/", + "4ClOrIlPPwNPRbYZqoIP2Pn4A479EMti+B+UkhiS4lGRDi8eZRhjGqROQfk5xDSYjovmWHSLC+A+cCwo", + "m2JeDOdDiiM8PEnjJC26Sf3QmcsP6R0mOR2dZrpd9pAWD2McFERyim+VoMKLZ0qiHJTTVPjFijgjPhMk", + "f3nGJuw2/UbBwbsqE8QjDuwfY+c3xzlWz0PK4uE5FBN8rmRlTHFe/Xyq1j0uBvlfWBag/pfSfCnOl/1/", + "Tb9NI8aDHMALTMesIKkLMsVB3tklzkQv83Qb4og4z0oVxjSnr0tgBUFchpiOw4LqLwkd44TxnOwvOQQU", + "blk0dQZ/hUlSLOYrrFY6zZF75ZGIiOI1hLyYpSuIhvsTMsmfQxJ7qfuUhMUju52y4sGB4PpLSsfDc0wD", + "B6fXEcbUwy5mryNMh2+sf5gp4Wn8NQfuWsj+GRTL5xMBPXP5+D9FOCCTnImrIr3NCeeROuj/J9xifXSd", + "6Y6mkMPE4EDpDGof2P/GrLknK3kDPE7NlGdFB5hiY3ovShIYfgJuTD9Z6VsMnFVKKgW/YDo8xXbTyQpP", + "cQCEl7q8gOntF2y1zKzQbIHvgPExKdW+lMP3EFnLcVGIaWR291RIjiO14xxclZ8DiDAxo8gL33AiMnO2", + "U8hugQ7fE7Oz5uUHijlzA3xRmHIrEeRFh5jfmdWQFx2lflT+7j3zMJelopP3x+VnQgOwu3FRyHgwfM/u", + "yl2eQuSxlFcGcnb5a/lZ6TClknOolvxXCkBFZFdvXqzno1wyDWgF5VdYxJiS8kA/EV8yXin8FUR57P9U", + "UuEdoXpe33D8jURbVlexT4dQKHS26AjrVuyTbfPgSM37weXVzweH+hemOFACiKGVokSpeIal2gLVHHBa", + "FJwypfIQp+QM7kYspYHFjy09xzrvc1FwicUtln4Id9j5+J/prV61ByGJYOtAMWwKVBoQdJmB4DhD/4Gx", + "SR/pER1d2r/Pj/S4jsbTRI33iGgsHUl/693pVfHrHwPn97b7u/Si9GbHeXB/7zq/95zfz53fPzu/Xzi/", + "Xzq/XxW/+w4U/W33d+lF6c2O+7DrPjhA9d1abiW3jgN43wG87wDedwDvO4Dn4HEAekf80D5fH2TIv746", + "yH5RpRYLPcPq+V9ppHaao5SzBLb2YzXbgT7DzIpowAyHyQrUArkNNRllRTIErSra5zcQjcxCKArGHGtO", + "l5dwsz9nzxxLIiI8wW5ZKgREbsOpH2IOpabTACeVEkHoGJzGD0IiCMXOQA9YAjTEpVqHqVcC6R3xOI7M", + "PpoVpcCpUdpsyXuIBKG3pCg5FhEM2Wh46mLIEWBtyS/ASw19UPIKoQpNTiGBifvEmfs4Jc7TCREec3o8", + "+ZJ60RejDGdFjAalKuk9xIpJj4uyUxxwErjP5hwsf+QEQhw7rZwSquWA7JFRrM0i+bPw2V3xXEidtuCj", + "iJzq55gTZ8LPWTBm3NhysyKOx6lDSRdk7Ly9MBY3+6TlPuw+KwGAE8rcMo6/wKRSIl1MX5J4BJwlzJm/", + "y1uWfHG7YiN3VJeS+bchi5yVdIWjiFAHc1eEm43eeRalTq6jKaZs4uL3+ls4Zpw5U/QJB+k391Hp3E43", + "SpxzyeATiShJHSR/YtGYlQnvV8wFdmbtX3jMwXOfE8bZt3DqgP+vlBve8+6N/tO3+4DZAzL+nzFay7dc", + "nvVe7ydKL7w1auGxD3bfMWcBW/tUqQgUc6Lny5YehNYtIX/mREhjgsqKmF+qwWLGnRY+AB+nSoYrik5x", + "CO5TFJAJCLck5UQaPOZFUyal89UFpNSc2B4b6f9YcKxNgcUJxS840a8+3OEvOALNgE6IN1XvTvU2e3pp", + "/7441dusMYtvvcFfsBKfoFx0qVVKW/AOKBiB4uxf+k//4P2+auMMT/AXhYDzC7UznF9evTzXjVvBYWs/", + "0dScP6b+rZ2KrOgNS8eY0MwqlRUfhFiGegMpSowdOns2IoVbMDK+XPkzDYB7qRb6s7K3+BazEXNLyBfi", + "PqYUj4yvTVb0Dkc4saRRlMUeKfX+LsUBjnxMNZ6cUncM7xllkdkqsyJtHjWnDlnRB0wrBUTRSIxLYH1g", + "igrcAmfqs7JT/CXlrFTAv6YgsDuYUxLcYRdLZzjlLoxnJHU7OmN8xKLbUkkagzvR53jMhufGEl2URdht", + "9ZxIHxPugnvOQmq04aKE4gRKBVwOT42d2im+wJxJRscuEJeYmEVRFMTMrXCFQ1LC6RXm+K5UQzUpceLC", + "fcVLdPgrvoXSY2QOGrOCf+JEPbGM7hmX6VgTycXHA/33Q2+j5xoLrlJ+q/d0I3ldX27tR0ruzn5DqtRq", + "+8TJN0btq0Lwv77U66NvDz+LEqMGXF9uvcd3mBDz29bqX0rM9WCuL7dOiR+ScdaNozBcXzpqwfVljlQj", + "HLqC4a/9y2v1n2Y/WkKsOUiizIWw5mS40bvvC8mSiIxDHVxKgt7r3otQpnccT1L2UlDt6mk8EQmmjf6U", + "VyGg30nwO4rxFHmAIE7kFJGRDrXLP0WK/0hAIRaIMok8AIqw70MiIdisB4WswqUdYkyi0uempAkPRYjf", + "Ii7xkgSQMBalApQspr7RwzwOmvFEAh2nq34ZdJARIlIhhf69gpMml9DMWb7esHqTNZ3jvDc33ICzqENc", + "Utbehaq9On/jihNxNjcGpln+wjUSN2NtcQMmmJpaFxAZd/CQJJaSK0RnHIo7oaMUgqL674rELDLIHbo7", + "YxaIpjHWh7HgqLP4rU5e/3Ow970TgKJXD7eo05MLUubhfHByfHZ8MNw/PNX+Gvbx9Oj0zdGFloaOLg8u", + "jtVDU2RETOixaXG7AU50DjwmQuiQglpQQ6odKuznWQBc9ulyKGwJPDHvl2iyrb0iLKg5RJs3BZNdX+63", + "h5HZeKQm/3S9Pm3oT+N6wlKCkJoWLlMvJlKxtcY7KDzCdWwmlOModgbbO/3By/6u0j9KLKYJoIDgMWWC", + "iCyKaxYeS5UtOkdpFJ21slj1tsRnbVzRfC47jqY+xMS/wHQ8n92Wa2chi8IGUrQgMIIx9qc5gR8vsXPF", + "nDYPPIaA+Diykf3IxMUvjITEWXLdAracRfojwpZUNRarnhI57b0e4UjkZd+As5w3SBuM1i0G7bgeHHqg", + "VxI6z1FZ4+kBlviSpdyHxoVWvG4SPC71HCAOCQcB1CxIE6vLQejPkDYkrTy+5RBLjAxg6KMeDDpuFGzg", + "PiFcw5UJFDPXLpagRIw8EFGxGdXXEh/HLCAjUggyC3yaBYeedQhhdOtmxJzFxOXBpkADI47aXxfgM0pN", + "pLb9peXCgAj3EThnOra0YJzu69Jk2Om27xUdGDDmCWdZrdKYHRnF0i4Kik5m0/FCm16Z/Bs2PndmavT/", + "28XbA7S7u/vq80+hlIl4vbV1d3e3SUCONhkfb/GRr/6pGpvyXj5DW+i348uP6OXPg+3KJ4LpL4hgffW2", + "r6VdTAMt8fbNDrUZyjh6phO1CInjBA3viAyHKAvvQ4SaipXL8NRu96I/2OkPfr4a7LzeffF67+d/Vfe9", + "TLR2JhW2siC6TkpNbeNzKFBVzuLTd+z/u0oHHWf7uFY6Ew4BwR6YpBGRcbSOWTB1op4pk/tJEhEfe9ro", + "VKbPvKNsGBlQSO/dTRwi5IcgJKFFRGeNDY4ididCgIZESc636PgQsQlwTgJAI8bR2+wzMX8Xo6wxm9Ws", + "5s+YwdM8VQxEGskF276wH81pvbKYCzwV3WZDc9b0IYxwGkl0AQG7R0EZDEwDlADvxyyAKAdKNOaYqNsY", + "vv3sf+HfdgbwPJ6kGj4IeT4PlyAloePmSSa+jXeuYkkqcYFqldpkmkAiZGkUIA+QsKmmsO8zjqkPSK1J", + "dHzAAthBnKWa36IsU4nYQIyrhu4ga0SAvdPSto2lzvQGHCUcfKKklM1ePadBNVBWA+/gOB8zygbdxDYh", + "5KdY+uEpCIHHcAGjVjHgVM2Hu6Y/8sBkHqhzAuanOieRK1M6K26iBJZKQoQzuGtoqzLKAg63lVKHDgqO", + "3l9UBteRiPD2zvZz+XxCkl3JMyLSTV3A1xREs1ISF93M2XWasV4kIRBdG7Dg2D1SfEyKoPTvJTwgXR3Z", + "+o1osDxU9f29GS306yCO4ufB12B0N2hCSwMcdVMIJxI4wY0p59SOJBCm6Kb38eKmh2INteJHOjeN2tKz", + "VFuZxF1T708vlFZ/enE2PPz4prfRO/z4Zvj2+uTkbP/0qE5hzSONx2T7dufLlH3x9271SMtqf1Xor7ew", + "zQI+3v368tu3gES6BUavtfqvEVYf/UerKiBspCfJFHtJDE4gQHchUISRMSFYxIRYGNOe9v5Qa8Li4Ohs", + "/83J0fDi6PzjxdWlQsLxZamkIx7Eq/TL6LkPe5PQVzRVtS5lc9kgr+lhKtZniaHj0tvbpT8TEux+G4f3", + "cYXGRMKogFUZ2LqutaxeJlxn3HT+EnV3m2bDXM9pr4FxZUPuiLsEbm+/DF7xCNNpvj6VkDB726N+lAbw", + "7vTYEKXemnOttEykxyOk9TBkP0LvTo+zBal3+YadarVKscPW1MjmbW7nnPkQpBzamJHSj/d9bd7SPHxO", + "Eh23/j4NjqgSQi8gYVyK+l7Xwka9XbaDX9zegofN7hIQodqx1/Y7zc0BB2jzZxW+muEAKaFEc1P7CbpU", + "sreQxBda7Do/fItsM0iknps1Y54U6O415Q4FOsVJRwoeT18EbFuGUbw7fp5R8EzqdYTH46DLoqzK+jka", + "G+wbZnqVxGZnSBO6GqIi3DHPNK261WxUSP7zQWoUT43d7DjI5KRZE9CMzPsXe4zfJXu70/SVYUC5joGj", + "6OOo9/q3uaBVucf3z6u2cSUZsWha6YCt8pJ2rCWuEHCvU2lpLSRg9123u2+725MQXoUe/sqNiK36DdII", + "Amd1zbQMVuqbrGxNfUXjEU9EIvbud19g05djZ6un8MGdtpwrPBbufDX3HSavXpDomxAi4ZP6tp6tBwek", + "2jy51OlMQQPGLOylNVHe69qZeIt0NiB3e8kgHu3sUS/jEtWRNxiciUAmJWDKraWSRlOEkyQiIJTA5axq", + "gbQHoVVRtYUrUzyqSQaD5izUivOVzk1xpA00WopVzUOg+hQQgS9Nslk81mBgIZhPsLS6pGMM30THIxTA", + "iFAINhCOIvONtrjaKuiORJESHTkkEfYhQKCTDOsUzpgioJxFkVKWEFMKnN4PnC4QEUjPtRDm+DwXsLus", + "oHBvEKc/p1/ub+m3kdkY5svKI58/B/FyN/lGfjYbooBE+9/xOmKPR0iA1GhClNG+cQowQG1kaMx06QwX", + "IomItDOqU/gWHWwswVmJ/0K+GOyScbKj+URl/1OkuChN3+2Gg+c8+ervPScTQ9OcM94stVg7RW712hvs", + "5b1oGgau9w+jXpZPvqyqBkFuohfozrpOZGrEbCXcNym4stbdtaztxQ2S2Fj7oEk4BT624lLjyFwW2Dlh", + "YNWgnLXhAPau3n/HedllX7dfCiz4NzZguq/6KVp9glIhWbzYcdyB/qbI3ieMsbF7A+f6m5IZ2xHnsyY3", + "nAyUtsBCO9f4ot9+rmGgALyGh8yrZI4YK0MOImRR0P0ErgzCVdZAJjgVx03PS+t7r2rgrwzRZppzAPq8", + "ZGrbRlmnbcJKE4UDfGmthc5slUtxgM85jCmm/vTKWryrZTumTGdtJuJ2XopQbQKiadyfYK7QIHQyuMN9", + "p9f9w/1ar9WyHVPm9roqfJVmuVmNFWQCTVrsciqp4xc1h4LTJAH+RnPP5Qj3Om+giaVZsnR6+dzEZLs0", + "XcNbukR2UL27lpyz9HUIvWqG++o4zHcbts+mMcSg85+GJLkA1Z+2hbXkhk4FcPSF6YgJhK2QheJUSGse", + "U/KRlcCszKT2vKzVer5o7Sd2yGJMWvwVnK9zsVHLkNYnUUMUYm3B1I0hm+LYCHLYwKXADUwvzhlcyITM", + "2E/9BMfg8DhIGgUiLdJgKTnxUglIyzdyA2GZX+yg8JChqpBIC6A11jxAOJUhUEl8rCQEPMaECmnaN2kt", + "5RQ5wn4HvzuLT2czPs3nGDmT3OtKDWJpcpAoAiwkYjT3tBEJ+PqUHhV9zSaS0suu21UrWX9v2JBm40k0", + "I4qP4cBNwL0auaoAJW+/q0D7c0gH22kaRnRwd29M6CBxizybNl1Bo71Pc5cgozXlC0hN3ohE0iVDR/TV", + "93YctLer31dbb2iphAMdBFXHPbEq4zxPtYe5HK/Eb1lBks6n1mxEl7r6KvMAq0Xa5sKsF3Dhx2wczxw/", + "b2zMrkiGWCIKEAh7SBJbB7bNBU+rLTAdHYJPSc3g10AEBcoax+ibVDfITEQ21NhpOtuGz4/ODo/P3vU2", + "ehfXZ2fm18HH0/OTo6ujw0a4kOm71+i0autc67E184ilaaPBoycVjSCa7mcicCH3ndLa+97QYZOXcMzp", + "TJNyMZbqDF6YN+j04sycx2nCUeyIcaTJRv3OD5aaDMPm/K7J1Dxi3DdNmzpUyQ0+o0JyTFznvTYXgPx3", + "3os7BRdni5okXr742d/9ens/3SXhK2M/xi2OUGe1IAG7FttcpdFZi6hjIGpJsL1+f8MMypk+hRmMH/m/", + "gLMWccQBSPt8aFGLBsCFz7jxGyEUIvT78HcUA9ZSy00vuxpMy4tUCSpYCDKmENz0NmcObLs0rp/cgf05", + "fNY4NgWWGgG6tMC0D5b8qAlpcwCdOUHJ03EdL/whreFNdHXoPKh92W7Qfz7Y4RGZDKIdOTVC5ErEBcep", + "cnmPSppGkXbVK/dSeFg+1Ln+ocFYT9k5fwlv+wgLeZ1EDAcXEBPFcR5GBP8G3vscJgTuFr635UJ/1oVM", + "F7iP5EkGEthbpzrixXoHZIAtFYSgvuNjkIcwIf586jeVA1N5bYGAeivKl7vL4WdfItLAxOseT01SrcXL", + "+qIEs2XWbX6apPViodbjA8/bwF9orAtJ9DMx9r0DaE0Sf9aoUh9bvEhi1qKwaZWTjXKpvjidDYrj2dyc", + "hiXCXD0rsU3blbBAqnXtXxMwfcx2Q3GSRFMk2R1Wwp7mlxGmfQ8LCPKDWNN1RGIiN5HNkaq+0sY4YV6Y", + "M+SBkuS2N26ol0qEo4jdCX2sO0plygHBfYKpvqZUAxynkSRJBAaufFxkpJXqXI0msVJEB03WFQXsCtBV", + "xpLGiTbtOGi5ofMQo4/14V67o4mi26YZEOgOokj3RafohuqIgrymvdHJTKKdqUXnCd0oNQ5NMCcsFchL", + "BdFqHAcs1HY1H7NW+BPLXvWY7ZG5EGlJvnbr48Kz5RWW3020L4xHboYDG6HvfH5Dy8SWj8wc0os01npC", + "FOVvDDhCtwX3PkBQXMFbRvZmE//R9eZQpe5u/lg3EIcx5kGk5o6NKiQybxarx6bM5FLTPMaunQZ/WWOs", + "nMFX1fsTTfNNHAxocNjhJrRqoFnrQp5N53PpWEjM5cIAVbemWahCJxaQmQibaegJMQ9MKx03pdIUaI+t", + "kXxAA9/bhjbLqbVZCG52QQhs+oBaI5SZmam9SLXi0PhKycoNLxoG0RgDXx9BlUU9fENRvBdnZzr+fHZ1", + "Q6uNrY1d1W2tMJPHz15eXfCU+3Vl8AoYx0DVUL2pG05pAjXVTgooYUIQL4IbakA00kwW87mB3ADRDaRd", + "lDaQDSrdsKFb5QjTpXl1PhC9IYd4Ak1zq3dzdzRLcmc7Iw3cJqPTuRy6UCfnXWScD83eZRyYVNFN2kAe", + "ct94qWOrXjTHg8tp2jbkDNxCx/PhzBvyMgJ+SfdukdcdPXQlGPXsXdPzQPScO6mdK6o73NFcfPa9hk9R", + "jKYdofYq2bp3y1xDkGunnZnQR+KxWsZJhE3Kjop1djDDqvzbzU3yx8l39ffs+/AfN+lgsAv6r9///Mf2", + "99L7mxtRrfIff2vMPZTG504sTotKny3g5f2JWq8WbVj0V3g8e5aOgxrVV6IuipYqN4Ku3BbudtV842fN", + "uNX4JTohbmzgrKw1blDUgit/9poXM8O89LJeLF4rhvmfZJ4JDcKMaI3BKl/vnMW7Z8cEf2Q+UK97/9hG", + "Pz1//vwZev78eX97Z3unaEq7xnyv8qnsy/mOkx1iQ2q+ZKZxl+bVKJAZxpxRtvjeUBONqXdt3Ri1tV33", + "8Y53X2fT1nR2aCAtXardjVpLt2PXl6p+jXz1Hh2baLbrzcvNDSSnCfFxpK0ugL6RRFdCIvVDJXj8/mpv", + "d7D9O2Lc/uxvvxjs/V5OgqRftKZBsn3ba43r1vG2w6AZ4WyzLaZd/Nth5E+mgJ+L6G73Z01By2QkMYe4", + "LT18ebV3G6fT22S8fdsUWOoaY1tTgxSSWZ50ZFaEc0MoHon3fB+8QZQO7koDbT59q58meh6TsjsLnDWd", + "LQGxL+Dbizvi+/dBKrtGVKSv+GjC+Q79EvlfTYgh3PvGJX1dgL7a3k3Gt97tt5934GtXQF9M4OfnX2LM", + "5Yvki/nojqhd6KlhtAro9/p2XFIrzChyvG9klOLK2Rb04nSBlw56uxCw//Lrizv+7TmXY3heIuDchSJz", + "KMohyWGrg3QVEh70zzGXUxPzdJ7bI7txjlEQj/xvX6fxLvNpjXNUN8kCJkcm2h4MWplltuDbvEeawu/q", + "TgtZJRvr6ppbKvwUB0D9Fjnavsw9SfNWue0bHeqg0cxbsVYBBQxM3A0ejcCXefldSNT+wgFlcTMBwiOp", + "lF2t2AjQUVrGVmPs9dkkbyuh9oX6s72n/u4OgiL6/7BrHCTx5O3zV3f+OGQvX2XJC3RvR22hspdAA4Sd", + "CGJtDMBFChwkmQ2mmzWUG9ocQN4A45eXwc4k/Lb3ajRISzCeMQlHboqRIrbiDO4cZJmnCxMn1xU34WDH", + "e7n9iu/cBzrIvrJzVRG1kRORs8xyUuBulGiX9c74LtmZCDINgL80oXIhNjGZjbT+LmKell9s3ghd2wgx", + "xlhmTxO18cd5SQTaRWPO0kQbwPeQjj3wsQCEoyTENNUX2yE/xBz7ErhAhBobm/5qE+3HHhmnLBVunVxw", + "Ov5d08fv279r56jfP9rnwe+a7u15jknf6yhE+28ODo/evnv/y4eT07Pz/7q4vLr+9Ov/+Oe/dnb3nv/8", + "4uWrz3/sfe+vsNYsrcs6sl1qpLXJb9kBf4P9wmD+7wIl4VRoRwrGUcTG+mfEirOGRS0BQqcJrtgB5n3g", + "pI97Svr4JWl2F1Ugv5m2uAl2xO3/+p//Lwl0/MYyWC7ZW+pDqYxBwdo2Dp2ksT3MZUk6WXjim2KOOs5E", + "Pg/d8gqUTVV1Cmtw0+rqgPM5h2gh00R5ybQYSxQGFrCSVJxzWoPc3EloFDRUhb+LPL1uyVg301r32+sx", + "x0n4+rNrlvvcXIyajHMLuD1J3MLoLtUbRbJaEKR+u4otWAxSB3dE5BbQ7wf7RrM+wBEZMU4JrmjWB+3Z", + "hS9liz+mkBxA7puwrDafViWu2MitzRld6Hq2qca+jMX3QOccaO7L2rANHaIJcO2mobZCH0d+atxaylkL", + "ais9JOPwXZT6TEAe9NdESfaVyTygdv/AZrZTKCcjhE3UvNr7VZO1aL7ywtQRfz/vaY9Ddrfi/iN9W1DH", + "7g3yPhncdcXxnOYJNWeewKfvV49enZBhMRyrT05WjmcNyALIrkZ6lDC/0UyJM7DYTDrtg3W2Iz9fVEsG", + "aBfr8xBLaGYFgT4ClRz7txaV2aGTszpr63FEuEka3JBLUvPCkY1e40JmibQClMXGdjnO2+iFWLx1u6l7", + "r4dYnOAOFYy6krlqNFb7mErtDXlJrDLckCUcdxmyqpWjbuExG8dmDUkGbseuzCwt3MuF9tqq96MEANWP", + "9erKHAQ0YdxhgazzqCKY9vwpDUcgUW06KjlljFVUKc4rQimzM7vsSGPM1crImll4xKxKWfOWi44RNNYC", + "a0fIKG8DcYjZJMuUWKBkYWRoYHK3njJEOpje+vpZwEqrw7JWyVDCdYJSROiIUCU5fk0hBRQUu/gSB/el", + "ZV9e400LumH1Oiw0yPjegzloW7iWpRST9xId56eg5eTUI9jZe/lyZ/sFwN4ubHs78HLX3xnVT0qbjkYH", + "G5XIrazT5oNQkSYJ16mNzpgsnD4z9aXRGce61ZeZfMlP+ZhOiAkpauKO313gsu5Rqf9GH5mqS3wNv78q", + "QjeLQecU0KsgD+k3iaUEwsi0hExT6Kdc4r6hMb4FLW3qZMvPLEkTXnIP+olswiaKlRCh5fg0Tp6hEWcx", + "wjc0IiJzwjUdKnmV2PRWnjk3u7IxKsiQJvBy3qsb6jPO1UPA9f1Teqf1WRynVCOIjgtPL9fT54be0KsQ", + "RJE6mQMSknEIsqSUChN/1wmtRiQCJJjxJpIhqD2cIg9uaMKhb6BRrIwiCvea0WhccvB0dinAcVSkT7AJ", + "soy5sshAm9O0h6fari/17cf3sokUc5u7mZ0sUKLOJCUJoKRDNPkJLRjbrxag5BBDN+n21DhUoTzJCspT", + "5ChMm1QhZtqwhDHj5FsubI5N64XQaXuOjAi8qVZCnp/DUsz+/sHRZnfh2CQCEF1ipd6aqhYJ4UKj11bJ", + "h45edYmwQCAk9iIiwqVHne18B6lko1FXV7CO6topvl/dmCN2t5ohJ8AJC8prTZ92uKGv28GfL4I/t/eC", + "P3cHwbO/tZ0TzVAZr44Pj5bSF/Por/nGJvHA8KruqulKyTdXXVczoZ3V2pXSY6b2rmIMc1TinDvnxFtb", + "uOvXm4sUnJZROlKgYortyrTdfhyuWZuZT0SkOgq0GgNuOoMgnxHIZAQIakpzwFlyrFOkHFOzI54D921o", + "W7HYjT/hYPN5Zc2b8v/+p/nf/OfbR//ZzU1wc7Op/wb/+bfmzTiGg3en1wIevdtjuk/VrP2Ajo8KGeAH", + "9P6Duv1B1HVMP9nl/IO6flwS+17mMKOCgbSwmFnX/T1KHoKnmkZgmRwATyggfuk77Gpnw7qlys7l+BS3", + "0FXFn7ia0Ee7qrARwrLPibgtAglM7BujRciJdmxQQkV+7UU9F7TaR7tMV6HPlS9omvedvZap8UYJ03nR", + "XAVVs3GURnLmhZsT4HgMVrI4jVlD9M6+qYNsJWM3NOlstDZOBMpFoJz9PN983llYtHLcKaZ4DPq6HxoQ", + "vzlN9D4KgJNJZhG1PlUQa5ugQPvbBy4ULxaAwjV1d1O4OgbpV7hgSSo5JTSVzXnGUypN2o3Y1FEESyW6", + "A8xtJsRx7I5VHxp30GUaRaLajT3qhZp0xdVIDPM6H2zudsd0Lh1Vds2OQBCq47y1spKpABzTMTwcoPJe", + "uhg8Su9YJTjNQlx3mKw5ZvV4WhqilUOy7HSteqpqomd3UKwNd23QHELUeHpHRiPg2s/VA3kHQPOUspUG", + "KjxeW5UzfwZrjGVJwgTRRzojm4u8AH9nQegbpOnu2CysGKtEaF3KXhCiVdFbS4qXikBg5IROElQmc8wI", + "WO4kZzYKG9+bTN5VMGepDkr2vda5PFvik7LgpOIgQgBHx4eia3xSw53W+D6LTxpYL6wiXmmO6NwUolTp", + "YZFMe9VRbaKPUYCEnEagxqhPRbYH/YCMibQXZZjEkkSY3JUjk8Q6hHscwD2JfRwhXVtsojO4qzS1+7Nt", + "6rfr6+NDNNkrbrIFunlHbkkCAcH6Olv1tHVN9akOjqLp0Ax9WARE/jdrKhvuDX/imAYsfvasooj+Nui/", + "wv3R5z+2B9//zB9efu/nv/c6/N7e+f5sljtvFYud9T5Jiui/7LZbAnywPRj0zNvBTvFzt/i5NxhU7q51", + "Pys7GQPXZ3dXpCnt9kZPcjIeAz/tmop4ZuZD92yq0m7TCjTeDgprhyAxiZoDpdq16643i11X+6kDo2+O", + "8VNO5PRSrVybbAF0Loordgu0SVHIZ95WRFLX3OgR9T4EbHKdG4fa3n0/W6p9W7+f1c+WeUI+wNSEBhA6", + "YlYzlNiXDib0UTTj8v/OmlMLpugmA0qxC66qu7dFlz6ppYP4FTwkLMHo0059JCrc26ER9lgqrWux2CiS", + "LJt74MwZsJPHNiI+UAGFX3HvzeVhf6d/EOFUh8aWYRwTGabeps/inHP2FTsw3Wx5EfO2Yiwk8K2T44Oj", + "s8sjl+nbPONo//zY2LTNIU1ve3OgF6GDfz3I7h1rL48EKE5I73Vvd3OgW0ywDDWhbE22twpMqJJx043P", + "FyBTTgWKrHMMjqICgbYBxN2UZJkAJKZCQryJjpXKRnGEgAbaXlL46pmIEJ1GOOUJE+ZKRbWS7B131itn", + "P4oOClDVIDjWR/Si1f26qLJlBbDvG3NrmnQ/HSo6IZuXEvNFvzmigb5cjltzjUa/4pt28ViBCpubvxUq", + "tr5YlyWzSXeWQfKpas8xV87IX1tgHz8YVpMlVTRuUvtRhEpTYixfv/WKvPnZvPc+q+/L5Lb1h0lH/t2W", + "zac/3ECBIg/pIVj7YWB7sQFio0Y6ssC9ZTyHff3U9NB5XnJ6rdms43xmbEitShc31Wn9/H1RfNm88zpY", + "oY0ICJ1kZ8lra3zrD/PjOPi+fD/zJz3rZDZMNol/AyB6G1ZMutgd88z9hZpjpLmCRqqCxGeTBqDh/pSM", + "GWshOOfIkiFzDyKicJclmIJ7pd1qddiEz7VdUrCJshAqdMfo36V7TQFKqSSRm7cqMAKNFq8TllhXTu2X", + "lSf3144zalEQGU3RLfFvIeiz0Qh5U+RFJKlvEyYE8QzuDKUe5bD31r78ui40e8Gqc7NIZ545kz9yAhNz", + "WWvGIv28wVYeuDDf67w3dmeQeTTnY22587+AkGfBrI/AuGfx6I3e3gr7y+9XbOjtDQ7yTBK6291H6fYt", + "4x4JAtAu9c8faaw5/1NKJnBkLnFs3xCbtkAllnMWmUvjro4Pj84/fjwZ7h+eHp/1NnoHJ8dnxwfVR/Pf", + "8f6Z2UEbebPxeEfY4cG1FWzqHGQvbQ6FNyyYroepfX8k7rlRauc+jsrNVFXf2SumC3nPp8XFCcdOYD47", + "MymnyuENNxz6LICtP3LO+H0+489v/zLYyW5fK+Lba0T0Duwu8GZ6mbPgJ7NNvoNs9andvhxxPl8abZCf", + "RGmMbSLUbMvM58pk/ZG5un03MxNBY4yOLi/UFHt1Hgct/FBWJJvMc2hakUqgnzwQJIAsnbMtflaXfEwn", + "DksozeJew/01DB3YaS1j3rQ0g3715badaNGbmqtQWgjvR1LbRi8i9DaTqPplG0gZ3uK9yOoF7gdl6suz", + "WKpP/2ZZ8+bXFPh0M39jeOoPZlTFElvT/raQZJl7jWq1JW0MRNJRYJgiuLd2/Jb90dR8tP1x0W3r+4/k", + "sj+K7KyRWpNC2Tz922c15QVd2nleCWnW9teCZXe0e7bpVdntmZtohoK1lKVSAOZ+uFolq7PeZkz1HSqq", + "CXgks9YyRqwWg+RD+dJsc4prSfHrcrwxprWL8ub9+rgVwXS2QD8HvSXJts0y2GW9ZaU6HfNM2elCB7iW", + "TbwmDq+N97uCUIbQhUdakoHabaDzxKDcDp3xjDGZGHcSwrO7P9vFo3bo10ITC4jkq7IKF2tro2Ndk8B7", + "nohQkQ+a1p0rIjz1dVfaEBdfdxDyLTGl/uIG7w68b58iUmN/xvP0jkQRSlIR2qh9CULmGk4WUq9P54XU", + "xzg00Mn0spyA2tzs3l5yFzKbFJ8i7EsyASRSL4fGZHTDvkxxZIJmidD96wtxptQPOaMsFdF0E+0jkfo+", + "CDFKI5RNib6U0ibfV1043yCJxa2+59wDoE5iQwVk6Z6F0pWgVg1vBveG3tBfFY64Pt1Ce4M9HRj6lqU0", + "yO5Xz8zlWd7EyviP3l+gIplmXS28nFL/6P2FDZev0N5Ow3T6PiQSggoFqmZ0X7ahWTZrJZ1lJFGkPWuh", + "zexYpiyLLU2li3T0oKOgRThX22FQd/iK0ll7ZaMYYiorijGtBc5Gmuc+aNpBj0311WyktrHlNtTGcfFs", + "l60Na7NpR+04mh+3sXbF0KOQqXYQ8cOOU5FfKFOS0tQLLePU52M/+yAf7BW7FtrjaB2bMG7p7tGM2wuQ", + "Qo6aghAUJi12Ftn1M4aSOHkt18hXs26eFle1OnpIkiEHITnxZXbrakc/E31Xfd4KclsxgoKWcjxAMMFR", + "am5VCoFa2kdfGKGF9qK3/I80yk7PEQ5i9d43p+gghPEnz1ZXsx/UaQ7OhTumNZJx3NjjQlp5ATSqQL1q", + "FT3tKqVKZtNWVSw61Xlu0R1mTMPqedjMGfj+ZKfeai7dJ39pQTIGPoa1aDinqmWlAIzFRq6LuP6jG1YG", + "MRpMceIm8jzpJqlQi8FCt79WW3Vc9PAgPVRD2m6XXX7uFvGDcrJLrMATynp762kilEiCo8IBqTZVtvax", + "qei6iK9j2po83Ne92it9zV3gGf6acLeAY1OdGmZu03oXLKo1bpLu6/XjqzNHzK2DOYDoUmKZzvUE23j0", + "tWEgBDd0X5jwm5ILYOH/V2dtpoUTXT0XaM8LJ/u1cLu562Xn0dZLizklw6xBjCPqO6h58OrJ/Uxbl5HS", + "O7Hjb+lNZxmly+zuqTAgNYbTrkxnWcEyy9re1Q95Mf3ZCqFaUtCswCyy9m3Iyp9r3n7y/k13T3H3ycTL", + "h286lr8NNSebuWCql1fX1sm5c/HxOpdK9Yb/rquldvv2KvWvjhje4jDiIMLHO5LQJkPdp7uf2at3s3uD", + "bd6T3JpY178vTBsd5niuHGCAmTcbSwvVGb6zZDQrR3PJUTXDp9SXiDYdcZ/nCW/WxK/K18mumVXVOmu6", + "oJrCXTTtW/fzEooaz9UrF7EuYOtz53rrDze1UMfDdQe28vG6iY3E43ExANFyYFCZ4A7+h/4s/8N52Fin", + "KdzFYPfD5llrwNT9P3ANZInh26nfonEt1K8Tj67XDl2nlWZuyegEuKysNX1Ioi/MqnNN80FBMlfM3ke0", + "tgnNbu6axc7UolVwQWDh1s6CrxoGjCllMqtuUqiURmwyaPssjQLEfD/l5izdpAYwRnRtfLdZUE36DyUa", + "64yz+iweRxxwMDVn8hywH0KwWWWuFgD33mfJUHa30+J0Jro442fHB2W9ucUCeEKEdDS/fyNvQcFsCFZz", + "Fnw5TfTFAKoa8qZZfgHtDO145jNu7i10rRRFigiTTsvNjuGP4z+9cfysMQdjzcJHYkDnJkGPZNk18Bs2", + "VeuGsdmqYTQDl6dvawJt8TzMdfiOs/ziLQBEWNgkhVAGo9tdCg1JhavJgbNUwi0A+ON4M8b3pb5ncZG3", + "EcPSZM9tBIDQZQAgdFUAZDn7ykCUM/fNhachOeCKwMtSCcZ5yj+U5/xzoJwB24ycgSuCsSGr02+DzUF/", + "e3PwGamig3enyGTJaAOynmrvUWDzIGJ36Pkeisdbwckc+GrprR4RxBeDjiCWU/M9CoSEmlRd6PlevzOc", + "PwzIF4P+9suuUJbT1T0uoNsvB/2d510hdRPBPQqc2GMTQN0BrGare0QgdzsD2ZBJ8hHh7E6XlYygq9uL", + "dfrUBTl2lpm1KxTHtAMMy3HmNYKyIAdeCyTLctr1ArMER10vQItzzrXAsyyHXCMwS3HCNcKzMMdbKSwX", + "VsJfkN9d5CL3CmFYjt+tEZQF+d1aIFmW360XmCX43XoBWpzfrQWeZfndGoFZmL+sFJbMtJBZEhLgKMDT", + "+XaEQ0yi6aKgzBHurpjUdxTltJObsloRo75YKUIMDCFLuTChUCYsqwMs+ptfiQxtxNHKgAnwdFFY1Cer", + "BeVSYhpgHugLKvO8xiWLVDc7lLANHWbtrIp6DhiMRsTXJvOPI/QJ8wfB6RfNfRzlja1Mj1nYqOn9aKOm", + "98SMmt5ajZorMMp5T98o5/2bGOW8fwujnPdvY5TznrpRzvt3MMp5/yZGOW/NRrmFFETv6SiI3hNTEL0n", + "pSB6T05B9J6Kgug9JQXRe0IKoveUFERvLQqivoNJ8eKl/RLMLU4rQkkBzrJeCusBBzeK98s5KqwUwnV6", + "K6wHlXrTX85FYY0APcgvYe1wLeeMsEawHuiB8BiQLe928BjQLe1rsEbgHuhgsHbIHuJVsHbglnUlWA9g", + "8YMcCxaDqZt8Ea/CzWD9gC3ndLBOuB7ogvAooC3vkPAo4C3tnrBO6B7orLB+0B7iurB+6JZ1ZFgHZJkK", + "59sYws4eDesE5kH+DesHbDlvh3XC9UDfh0cBbXlPiEcBb2m/iHVC90AvifWDtqzPxDogw6vwoFiTsO2a", + "yzp6UawDRbLJp6KrJ8X6ACr7VXT0plgLOCaPxpr8K9ZEWyEgx0VipU4WK4V4XrSaAiPCQqrJfctZvIKI", + "taP77l1esRV0+ACbtPe0bNLeOm3SimYb7dLLOpv8aPOq90TNq95TNq96T9e86j1t86r3JM2r3pM1r3pP", + "2bzqPap5la/CReSH69jek9axvSesY3tPXMf2nqaO7T1dHdt7sjq2twodexFF0oA105jprU/NnqfgeI+v", + "4HirVnAOWBzjvoAEm8vZK8lqdJ6e40Nh7tMRhI6jTOH4ffi7TZxirucpruYhMmSpRJhOdaZshIUgYwrB", + "/4V8FnuEEjrWX2sLge0AEYE4fNFXn27e0N5GD+6TiAXQez3CkYBmdOiUPS4SiIRYzJt0c9fJcfCR/ws4", + "Mwk9M0xhzvFUPQs51XldFFp73fFmU6UJIuEheDN5jWYiLutiOczpDn4M6n4NQYbAS3c5CaU3Tn2IiW/2", + "FoEEROYmIzXA/6BM/scNVXrm/uF+YdOxdX+CzfEmwgKlAgLkTbVCenV8eGQvkHp2Q0Wos0t5gFhMZI6r", + "JtyoCmeMZoE5F7qPXkPeZI+xCDA1iZPXnVtNXNgOFrprYUYW4bXcf5hngvRTIVlAcJTlhDZ3eGWzPjsz", + "5L7v5+k015YZ7/HS4nWdsUryxwILy6Qm2zLcYyjxuCEP5A9PgrevoWvMgZd6Alxm2pps0rThpsUzCFpz", + "VnFJAkgYi651jue2dO/7ih3WR2HSaiu+LRmSWmQ3OWfx2PL7WFFMb7lrGSxW3QR3Vyy7PP3hGcVz2jJp", + "RJ8qbdm0oU25TKMIaTbUnciqGU2VpPeEyOwcC6EvAczILRtzmcTcETt3CWaj3kQfYyKRHQbyWDB1P46i", + "2gdLEmg9oytSCF0DiWZ02fFG/gxtpZS3m/oebMJBaKRl6JFMyRExDkDJGrh0gxOy+aP9lHOgMpoinMoQ", + "qFREAEGWVV+1EWPpl1NQk2BzNv2t5Dr/oq1lLrJ1Lrdc7Cbb1iH8sP3WSYH+o/IKL3iZtNopdBJppQrg", + "CtE6l+Gn5pK4GSKWWnVrvGjPd/t68pKWxt9RhrzWG/YWTP48K89rKQn0/2lybjnN87LyrcPgt+y9t1t/", + "JJxNSJDfdfIo67dD5RyqGav9EmhgU+g7G427PUiWXfCLsElZkTXclDJa1Tsv3j9g37CNIae1Fe7OWwnw", + "mAiRXfP0aDx3xlp2QLJ3QGebXYgFYhNwVNni0qPjkbmk2vkYcyU2TNitvngaYcRBpFEuhpmU6BulWU44", + "G5EI8tskuU7NHxgmVchuxQWSuSHFxxT5oTbN6wYLODZnM6BzZwbWyoucjh6PLdU7XZBDoTJ+HsytnInZ", + "+qN46HBBg06Yro2JxWf/mxJoSe4tJmCVEjAqNfvD5L+NxgvckvKQ265wA5rGCurM3qZqTwjc9TZ6aRIx", + "rERxyiT0PtdPAz4vSLdc5x4Xs8k0T/FvP/y7QBEWSrc0icuRueHK7myqlKUCCZCzKeDC9r1+dmF7msEq", + "Nnp7LVRnBllctj7vSgQO9iaIgIxGwPU1RNkNcH8XtrnZNFwg5ocqMDM207nEgKl7Ff3x4ezN6ilRwqxN", + "Y9bELLjwzEoecogJVfLXY0pJrbKqQAYslIFVEVdrs6g+utafXGQDWcqGpAVl0xByWloEy+ZYSKzxquB3", + "QNXY9ZVKIuGAAxECSHsgpRV51bnSF+Uds+QvGuwmphl93+6F/nZNEtq43tODbgjOIM9Me+bG4HwIi8yW", + "AD4hPgytHLOeO8z2g0C4tjolWfmRZVoWgvwsS0tKSlMr2Ja5Hbsux+wHwaX5er2nWrjaz4Nmbz8IkG1u", + "xjnU0jfSCZCS0LHYgpDPvYH/6P0F4hBp82n2YZOB8ej9xWXxem2bA4Q862YRQ6MahQPegqhc/T34drdq", + "Qq49qp1puqoie/UEXcfzcqTsDHR5/Hcg5ZjT+ddBnV6czaTh04uzx6DhmNNlaFhB/wRpuAJW4w25Fbyu", + "4Y7cGkofRK4LoLoLcS55qe1MWnVvPn0Mok0a+lv6tttZyH1U4m2FaoYKVEf52ix2Tdh+EGF3noWuJK49", + "2dZ6q629ILDpNC2/h3H1E6AGpjuZdX/94974mF9g67g5Np+s4WVuVdRTufWH+q/TqX3b1Ji3zVdkLnH9", + "7IyxrNPAYtAwl3+04MC8XTN5/niyzO6UbSfIKpqWJ8iHWwsWm/RGpmTUaHNv6gYigdJW9TXlhJZ8VDwW", + "TDcQodYmo72F67WvL07qGqvuYs2U82Z6HPx46tETOot4DLblHUOZw/RC5JMmCQchIBhSphBvxrCe3erI", + "ud7erIp8XDkYqAxGC8/Iq59Vaq+DGGb3+VBho2gXVQezyERKEsDQWO663PhrbXzunb9ZrINA3tR4pvtY", + "wphxAvV5uCJBYeurEEqXG23n3V874zRppffZdgwuWSzYox34hSIYbOxCTOix+Wx74UCGPD4IxYTqi6nZ", + "yBxosJE55tA+GtajMPORmXG37yGW+CCVbDSaOc7VRvo4YRIOTSpq4tr+prYKBavaNRgPgOtjZBtEgRhH", + "ECdyak6IAxjhNJJVAndOilWLEKA+EgDZBd9Z1Iolvd96LXc7NN+n0JbMviUve8BZckyvGi8TaLqINgZQ", + "CoqpqlXDLrRaWtwNFJqd2i4x0saM+CtGwrKoqRBhZUUprXrM+qqsL25J0meaNnHU1xsX8IzU7/uKvDRd", + "lYu+AWe5Pj5vdY6Q/lQRJkVw70dpAGhLU26FL1OWXwBUCRxqW622uTNWuweoEhq0/Jj1EsZRxO6OVOEn", + "HKWQVV4yFu14lDmMBRuIlTwzslAzHYIhUQSKfTEKmR+I8fG1YWkcEKEaAY7YqdGF9qnhB1lWEoHImDKu", + "OsQCkZE9ILRbGroDXgnIeoTgtaawteWIbp1hX1ILAYuFfH0icGfId8Y52wMsWbMkpIcdSy/gA12Xb+05", + "lYahWaa9ImvzdHY6eKi0aqHsYgjLw1XmHZ5pf69slUuG7kJigw6Ko8sQC4R9H4SqUcPgW0IDJy6jMsNN", + "6zPmtIknurJJY3Az4ToVGMz7uK7I5ZotzxjWHeO3IsF+WyR1/v44WLw7VanWkQPE/D6vVDeNflzZktjo", + "QcgvWco1iJ8bJbw5y4uNRgJkF7NDRGIie48RxmqI+gIio4iFJOlsw1eEODugtbI4Sj40EPItMaX+4lyq", + "i0PMvpKTq5zKrDUt/CapMKsuwhJELoAgOzokJJZESOIL7Q11fvjWbql60apFfPT+AgHFXpQbLMw5bbGg", + "szb1cqYI+1LpJiL1ckBNODr2Zaqz9CmRR2jQtPPnlPohZ5SlIppuon0kUs0TRmmEMqpAMeDcJZqWvkES", + "i1vdtwdKVPBDCNJIb+s3dB/tDfaKVmr6ABkpIawBYuMz6gEasZQGesBGFHHUqYqX0ZT6R+8vdFIGxluD", + "jnYa5tD3IZEQVN2NptTX2D/U2hzjMwIXup0iN9Jn3uE6yPO4hTbNRmqi2zJSJNTdNPLUIOaDImbNRnrN", + "8Sy/tKNa6xmV7WTR06lWN06J3ipqa9yka+NaZLfe+iPVwZzfMxFq5klsEXYpELb4FqUIvBruT4iwHFaU", + "iH8xivrfbN/QiRAOHJa5ypg/M6GFbFyfasVnh0Jv5EtYfvP2W06BWuRjAbzYZDSnNxDkEUMoAIlJJLLl", + "bsI3sRDMJ+6Jil3+c5a5Yo2XdojrWepB0cOa17k9wWQ8UzNwCYHVNBZd1j+HgN3ruW8THkoRnoT6LCZ0", + "jC7UdygGIfC4wU58zpnaoI/eX5yaKg/AvZUujY68vCZjIFa7ZXYe4aAoR8yGqdfbmOly4SJvS288M1FY", + "l790Nhjtaas+1iGz1Dq8mUMykzfmDO4+8gD4M5N/IovmpkEuZCgR5riI5rTW/DBfTUG+mnzj158JLMYs", + "mimoWuyxVtXflUCUk+Dvujv9Xn2OpTZEFEHirgbX1K+G8DDVcRUR9m+VcpJS8jUFqrQ6n1EhOSaqBWYM", + "NqcXZ7rPw49v0IhAFAhE5N8FSpgQxIvAyHhxGkmSRFCTBpzY9QwULCUnXipBbKJ9PVCRRrLB+JubjKw0", + "qMDQfWutFEeRmimLM5FVI15E5NREVkjgMaGAQqZDLUJMgwhQkBr61t8YppXPm8GFhZoId3KykeU04nMi", + "gROcA46DwBjG3Oo2NZmirlEqU64FIktQSqJWLWm2wSjCuTT8zBi5KuetqmezEPZzVXt9TpW6uwvT9iMc", + "wRY9zrVlLciodbOZezkudMOl5XLDaSbAyWjazmqao/IN4/NDHEVAx4B0KxZjtRn/pLtwOOTSnNa0tDpm", + "a9on2uvBdFWRVts8cto0jMDm11hCw8irz8uK4SoZ3QSC1qhBHZzY6Aa0mOC/qBiZI7uQJJXk16wxdJ8D", + "zcSzKcB0arSITMrT+rXeyzI1Q4Yw1fZ9R8tQ4o4JbkBslLHUQgMsayTFHlnb+AwYLhPt5zJmIdU3dWDP", + "IWI1AJ1kRd8mpp469JWbObv2pn7rwxGbz2XGmF7f0H5jX5amN1AEeKJjfIqcO0WeP9OD0wbW+z/tF8nT", + "ijXiKD7lJD7q+1uAxOiLWRho+YsNpE+SstQgWo7wI0ziep42SxGYIogxiVpazysrvqv3OZgAtQc8UyUG", + "/a//+f9pOUh3AwG6C02SGA6ICPM260PtsFzJKY76kYtYOI8+aeIA1wJ4kZFJLMtKdWy1asuc1hetrXSR", + "u7rckocdCveZzIeOjSBl5Q+iZJipQjncS6BWZhFpoo/28/RjxuLTps+ZcSpMHJpe1iSHGDCcflolkUXC", + "u+05ip7IAv4uGto94EiGW65/VLsE4LoSoTvwQsZua/j8H7pFt+pyxFnq7Ne8s+UkHDvMhIN25Wgf4lUI", + "6NzWKqjR2mIJNfkC9JLGyFcbRm5XsLqLZjFJhH0l3xvHEcXAUxxFU724rRZw9P5iE10ai6+nOJPqQ4nS", + "ee9vGY9NaxyETmcQBMQcoiNCjTOMToPANtRmxcEHMlFAJqnJ7LNRg9GDEeMOYHZcGtxgs9q1eosjoZN+", + "kTiJ9NXEECDBEM4Am2jAsvY0E/FAJwPSbSKgknCIpnrDCaVMxOutLYFp4LH7TTMrm4Rt4STZwgnpB8wX", + "/03i8dYhGROJo/4B5rCVYBmKfPK29Mw1k102guVIrjT+1dEcG3Mca5JLWyyetg66vjhpGdg7kOem0jWP", + "eksGpupMNqYj3cgqxiW6DEzMHZV48JjEQwdkzp227sDb0kH6fDOUcdRqqdY+B8V5lbWPlQ5pZmbCUt+f", + "H75t83ds9K4qzkjbveQ6HjoXpycraIyDquFLCIaS3QJdqM3PS818jv5Wj495k6+aAz/lRE41xgXorCNX", + "egCvf/usAFNCb7MHqmptzLP9LeVR73Uv429KDlE9bTqVNrO0lpuMjxvcthLOgtRvbA4nZN7XAUy2a9+p", + "ws0AJvM+/orr337F+lOIWKLzss5tYqehiZ0ZTXzOJ6zm6a+vwc9Usw3zA1PhWia1HGeJL5vvOlRZS4yO", + "iN0tbYyvDWf3bZjRBhIhVuSotngiQWwgkL7bh9tEQ0/758dCG2K1+Gls2VakVXu6N0VX2eiLRnPyrLd3", + "nnoR8XMBROSihzc1FhenGWPl+P75+/8fAAD//w==", } -// GetSwagger returns the content of the embedded swagger specification file -// or error if failed to decode +// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, +// after base64-decoding and flate-decompressing the embedded blob. func decodeSpec() ([]byte, error) { - zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + encoded := strings.Join(swaggerSpec, "") + compressed, err := base64.StdEncoding.DecodeString(encoded) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %w", err) } - zr, err := gzip.NewReader(bytes.NewReader(zipped)) - if err != nil { - return nil, fmt.Errorf("error decompressing spec: %w", err) - } + zr := flate.NewReader(bytes.NewReader(compressed)) var buf bytes.Buffer - _, err = buf.ReadFrom(zr) - if err != nil { - return nil, fmt.Errorf("error decompressing spec: %w", err) + if _, err := buf.ReadFrom(zr); err != nil { + return nil, fmt.Errorf("read flate: %w", err) + } + if err := zr.Close(); err != nil { + return nil, fmt.Errorf("close flate reader: %w", err) } return buf.Bytes(), nil @@ -386,7 +397,7 @@ func decodeSpec() ([]byte, error) { var rawSpec = decodeSpecCached() -// a naive cached of a decoded swagger spec +// a naive cache of the decoded OpenAPI spec func decodeSpecCached() func() ([]byte, error) { data, err := decodeSpec() return func() ([]byte, error) { @@ -404,12 +415,12 @@ func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { return res } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. The external references of Swagger specification are resolved. -// The logic of resolving external references is tightly connected to "import-mapping" feature. -// Externally referenced files must be embedded in the corresponding golang packages. -// Urls can be supported but this task was out of the scope. -func GetSwagger() (swagger *openapi3.T, err error) { +// GetSpec returns the OpenAPI specification corresponding to the generated +// code in this file. External references in the spec are resolved through +// PathToRawSpec; externally-referenced files must be embedded in their +// corresponding Go packages (via the import-mapping feature). URL-based +// external refs are not supported. +func GetSpec() (swagger *openapi3.T, err error) { resolvePath := PathToRawSpec("") loader := openapi3.NewLoader() @@ -435,3 +446,22 @@ func GetSwagger() (swagger *openapi3.T, err error) { } return } + +// GetSpecJSON returns the raw JSON bytes of the embedded OpenAPI +// specification: decompressed but not unmarshaled. External references +// are not resolved here; the bytes are the spec exactly as embedded by +// codegen. The result is cached at package init time, so repeated calls +// are cheap. +func GetSpecJSON() ([]byte, error) { + return rawSpec() +} + +// GetSwagger returns the OpenAPI specification corresponding to the +// generated code in this file. +// +// Deprecated: GetSwagger predates kin-openapi renaming openapi3.Swagger +// to openapi3.T. Use [GetSpec] instead. This wrapper is retained for +// backwards compatibility. +func GetSwagger() (*openapi3.T, error) { + return GetSpec() +} diff --git a/api/gen_types.go b/api/gen_types.go index 4b8cac2c8..7fa6ec8b7 100644 --- a/api/gen_types.go +++ b/api/gen_types.go @@ -1,6 +1,6 @@ // Package api provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.8.0 DO NOT EDIT. package api import ( @@ -9,10 +9,6 @@ import ( openapi_types "github.com/oapi-codegen/runtime/types" ) -const ( - SessionTokenScopes = "sessionToken.Scopes" -) - // Defines values for ClinicV1ClinicSize. const ( N0249 ClinicV1ClinicSize = "0-249" @@ -21,6 +17,22 @@ const ( N500999 ClinicV1ClinicSize = "500-999" ) +// Valid indicates whether the value is a known member of the ClinicV1ClinicSize enum. +func (e ClinicV1ClinicSize) Valid() bool { + switch e { + case N0249: + return true + case N1000: + return true + case N250499: + return true + case N500999: + return true + default: + return false + } +} + // Defines values for ClinicV1ClinicType. const ( ClinicV1ClinicTypeHealthcareSystem ClinicV1ClinicType = "healthcare_system" @@ -30,12 +42,42 @@ const ( ClinicV1ClinicTypeVeterinaryClinic ClinicV1ClinicType = "veterinary_clinic" ) +// Valid indicates whether the value is a known member of the ClinicV1ClinicType enum. +func (e ClinicV1ClinicType) Valid() bool { + switch e { + case ClinicV1ClinicTypeHealthcareSystem: + return true + case ClinicV1ClinicTypeOther: + return true + case ClinicV1ClinicTypeProviderPractice: + return true + case ClinicV1ClinicTypeResearcher: + return true + case ClinicV1ClinicTypeVeterinaryClinic: + return true + default: + return false + } +} + // Defines values for ClinicV1PreferredBgUnits. const ( ClinicV1PreferredBgUnitsMgdL ClinicV1PreferredBgUnits = "mg/dL" ClinicV1PreferredBgUnitsMmolL ClinicV1PreferredBgUnits = "mmol/L" ) +// Valid indicates whether the value is a known member of the ClinicV1PreferredBgUnits enum. +func (e ClinicV1PreferredBgUnits) Valid() bool { + switch e { + case ClinicV1PreferredBgUnitsMgdL: + return true + case ClinicV1PreferredBgUnitsMmolL: + return true + default: + return false + } +} + // Defines values for ClinicTimezoneV1. const ( AfricaAbidjan ClinicTimezoneV1 = "Africa/Abidjan" @@ -635,6 +677,1204 @@ const ( Zulu ClinicTimezoneV1 = "Zulu" ) +// Valid indicates whether the value is a known member of the ClinicTimezoneV1 enum. +func (e ClinicTimezoneV1) Valid() bool { + switch e { + case AfricaAbidjan: + return true + case AfricaAccra: + return true + case AfricaAddisAbaba: + return true + case AfricaAlgiers: + return true + case AfricaAsmara: + return true + case AfricaAsmera: + return true + case AfricaBamako: + return true + case AfricaBangui: + return true + case AfricaBanjul: + return true + case AfricaBissau: + return true + case AfricaBlantyre: + return true + case AfricaBrazzaville: + return true + case AfricaBujumbura: + return true + case AfricaCairo: + return true + case AfricaCasablanca: + return true + case AfricaCeuta: + return true + case AfricaConakry: + return true + case AfricaDakar: + return true + case AfricaDarEsSalaam: + return true + case AfricaDjibouti: + return true + case AfricaDouala: + return true + case AfricaElAaiun: + return true + case AfricaFreetown: + return true + case AfricaGaborone: + return true + case AfricaHarare: + return true + case AfricaJohannesburg: + return true + case AfricaJuba: + return true + case AfricaKampala: + return true + case AfricaKhartoum: + return true + case AfricaKigali: + return true + case AfricaKinshasa: + return true + case AfricaLagos: + return true + case AfricaLibreville: + return true + case AfricaLome: + return true + case AfricaLuanda: + return true + case AfricaLubumbashi: + return true + case AfricaLusaka: + return true + case AfricaMalabo: + return true + case AfricaMaputo: + return true + case AfricaMaseru: + return true + case AfricaMbabane: + return true + case AfricaMogadishu: + return true + case AfricaMonrovia: + return true + case AfricaNairobi: + return true + case AfricaNdjamena: + return true + case AfricaNiamey: + return true + case AfricaNouakchott: + return true + case AfricaOuagadougou: + return true + case AfricaPortoNovo: + return true + case AfricaSaoTome: + return true + case AfricaTimbuktu: + return true + case AfricaTripoli: + return true + case AfricaTunis: + return true + case AfricaWindhoek: + return true + case AmericaAdak: + return true + case AmericaAnchorage: + return true + case AmericaAnguilla: + return true + case AmericaAntigua: + return true + case AmericaAraguaina: + return true + case AmericaArgentinaBuenosAires: + return true + case AmericaArgentinaCatamarca: + return true + case AmericaArgentinaComodRivadavia: + return true + case AmericaArgentinaCordoba: + return true + case AmericaArgentinaJujuy: + return true + case AmericaArgentinaLaRioja: + return true + case AmericaArgentinaMendoza: + return true + case AmericaArgentinaRioGallegos: + return true + case AmericaArgentinaSalta: + return true + case AmericaArgentinaSanJuan: + return true + case AmericaArgentinaSanLuis: + return true + case AmericaArgentinaTucuman: + return true + case AmericaArgentinaUshuaia: + return true + case AmericaAruba: + return true + case AmericaAsuncion: + return true + case AmericaAtikokan: + return true + case AmericaAtka: + return true + case AmericaBahia: + return true + case AmericaBahiaBanderas: + return true + case AmericaBarbados: + return true + case AmericaBelem: + return true + case AmericaBelize: + return true + case AmericaBlancSablon: + return true + case AmericaBoaVista: + return true + case AmericaBogota: + return true + case AmericaBoise: + return true + case AmericaBuenosAires: + return true + case AmericaCambridgeBay: + return true + case AmericaCampoGrande: + return true + case AmericaCancun: + return true + case AmericaCaracas: + return true + case AmericaCatamarca: + return true + case AmericaCayenne: + return true + case AmericaCayman: + return true + case AmericaChicago: + return true + case AmericaChihuahua: + return true + case AmericaCiudadJuarez: + return true + case AmericaCoralHarbour: + return true + case AmericaCordoba: + return true + case AmericaCostaRica: + return true + case AmericaCreston: + return true + case AmericaCuiaba: + return true + case AmericaCuracao: + return true + case AmericaDanmarkshavn: + return true + case AmericaDawson: + return true + case AmericaDawsonCreek: + return true + case AmericaDenver: + return true + case AmericaDetroit: + return true + case AmericaDominica: + return true + case AmericaEdmonton: + return true + case AmericaEirunepe: + return true + case AmericaElSalvador: + return true + case AmericaEnsenada: + return true + case AmericaFortNelson: + return true + case AmericaFortWayne: + return true + case AmericaFortaleza: + return true + case AmericaGlaceBay: + return true + case AmericaGodthab: + return true + case AmericaGooseBay: + return true + case AmericaGrandTurk: + return true + case AmericaGrenada: + return true + case AmericaGuadeloupe: + return true + case AmericaGuatemala: + return true + case AmericaGuayaquil: + return true + case AmericaGuyana: + return true + case AmericaHalifax: + return true + case AmericaHavana: + return true + case AmericaHermosillo: + return true + case AmericaIndianaIndianapolis: + return true + case AmericaIndianaKnox: + return true + case AmericaIndianaMarengo: + return true + case AmericaIndianaPetersburg: + return true + case AmericaIndianaTellCity: + return true + case AmericaIndianaVevay: + return true + case AmericaIndianaVincennes: + return true + case AmericaIndianaWinamac: + return true + case AmericaIndianapolis: + return true + case AmericaInuvik: + return true + case AmericaIqaluit: + return true + case AmericaJamaica: + return true + case AmericaJujuy: + return true + case AmericaJuneau: + return true + case AmericaKentuckyLouisville: + return true + case AmericaKentuckyMonticello: + return true + case AmericaKnoxIN: + return true + case AmericaKralendijk: + return true + case AmericaLaPaz: + return true + case AmericaLima: + return true + case AmericaLosAngeles: + return true + case AmericaLouisville: + return true + case AmericaLowerPrinces: + return true + case AmericaMaceio: + return true + case AmericaManagua: + return true + case AmericaManaus: + return true + case AmericaMarigot: + return true + case AmericaMartinique: + return true + case AmericaMatamoros: + return true + case AmericaMazatlan: + return true + case AmericaMendoza: + return true + case AmericaMenominee: + return true + case AmericaMerida: + return true + case AmericaMetlakatla: + return true + case AmericaMexicoCity: + return true + case AmericaMiquelon: + return true + case AmericaMoncton: + return true + case AmericaMonterrey: + return true + case AmericaMontevideo: + return true + case AmericaMontreal: + return true + case AmericaMontserrat: + return true + case AmericaNassau: + return true + case AmericaNewYork: + return true + case AmericaNipigon: + return true + case AmericaNome: + return true + case AmericaNoronha: + return true + case AmericaNorthDakotaBeulah: + return true + case AmericaNorthDakotaCenter: + return true + case AmericaNorthDakotaNewSalem: + return true + case AmericaNuuk: + return true + case AmericaOjinaga: + return true + case AmericaPanama: + return true + case AmericaPangnirtung: + return true + case AmericaParamaribo: + return true + case AmericaPhoenix: + return true + case AmericaPortAuPrince: + return true + case AmericaPortOfSpain: + return true + case AmericaPortoAcre: + return true + case AmericaPortoVelho: + return true + case AmericaPuertoRico: + return true + case AmericaPuntaArenas: + return true + case AmericaRainyRiver: + return true + case AmericaRankinInlet: + return true + case AmericaRecife: + return true + case AmericaRegina: + return true + case AmericaResolute: + return true + case AmericaRioBranco: + return true + case AmericaRosario: + return true + case AmericaSantaIsabel: + return true + case AmericaSantarem: + return true + case AmericaSantiago: + return true + case AmericaSantoDomingo: + return true + case AmericaSaoPaulo: + return true + case AmericaScoresbysund: + return true + case AmericaShiprock: + return true + case AmericaSitka: + return true + case AmericaStBarthelemy: + return true + case AmericaStJohns: + return true + case AmericaStKitts: + return true + case AmericaStLucia: + return true + case AmericaStThomas: + return true + case AmericaStVincent: + return true + case AmericaSwiftCurrent: + return true + case AmericaTegucigalpa: + return true + case AmericaThule: + return true + case AmericaThunderBay: + return true + case AmericaTijuana: + return true + case AmericaToronto: + return true + case AmericaTortola: + return true + case AmericaVancouver: + return true + case AmericaVirgin: + return true + case AmericaWhitehorse: + return true + case AmericaWinnipeg: + return true + case AmericaYakutat: + return true + case AmericaYellowknife: + return true + case AntarcticaCasey: + return true + case AntarcticaDavis: + return true + case AntarcticaDumontDUrville: + return true + case AntarcticaMacquarie: + return true + case AntarcticaMawson: + return true + case AntarcticaMcMurdo: + return true + case AntarcticaPalmer: + return true + case AntarcticaRothera: + return true + case AntarcticaSouthPole: + return true + case AntarcticaSyowa: + return true + case AntarcticaTroll: + return true + case AntarcticaVostok: + return true + case ArcticLongyearbyen: + return true + case AsiaAden: + return true + case AsiaAlmaty: + return true + case AsiaAmman: + return true + case AsiaAnadyr: + return true + case AsiaAqtau: + return true + case AsiaAqtobe: + return true + case AsiaAshgabat: + return true + case AsiaAshkhabad: + return true + case AsiaAtyrau: + return true + case AsiaBaghdad: + return true + case AsiaBahrain: + return true + case AsiaBaku: + return true + case AsiaBangkok: + return true + case AsiaBarnaul: + return true + case AsiaBeirut: + return true + case AsiaBishkek: + return true + case AsiaBrunei: + return true + case AsiaCalcutta: + return true + case AsiaChita: + return true + case AsiaChoibalsan: + return true + case AsiaChongqing: + return true + case AsiaChungking: + return true + case AsiaColombo: + return true + case AsiaDacca: + return true + case AsiaDamascus: + return true + case AsiaDhaka: + return true + case AsiaDili: + return true + case AsiaDubai: + return true + case AsiaDushanbe: + return true + case AsiaFamagusta: + return true + case AsiaGaza: + return true + case AsiaHarbin: + return true + case AsiaHebron: + return true + case AsiaHoChiMinh: + return true + case AsiaHongKong: + return true + case AsiaHovd: + return true + case AsiaIrkutsk: + return true + case AsiaIstanbul: + return true + case AsiaJakarta: + return true + case AsiaJayapura: + return true + case AsiaJerusalem: + return true + case AsiaKabul: + return true + case AsiaKamchatka: + return true + case AsiaKarachi: + return true + case AsiaKashgar: + return true + case AsiaKathmandu: + return true + case AsiaKatmandu: + return true + case AsiaKhandyga: + return true + case AsiaKolkata: + return true + case AsiaKrasnoyarsk: + return true + case AsiaKualaLumpur: + return true + case AsiaKuching: + return true + case AsiaKuwait: + return true + case AsiaMacao: + return true + case AsiaMacau: + return true + case AsiaMagadan: + return true + case AsiaMakassar: + return true + case AsiaManila: + return true + case AsiaMuscat: + return true + case AsiaNicosia: + return true + case AsiaNovokuznetsk: + return true + case AsiaNovosibirsk: + return true + case AsiaOmsk: + return true + case AsiaOral: + return true + case AsiaPhnomPenh: + return true + case AsiaPontianak: + return true + case AsiaPyongyang: + return true + case AsiaQatar: + return true + case AsiaQostanay: + return true + case AsiaQyzylorda: + return true + case AsiaRangoon: + return true + case AsiaRiyadh: + return true + case AsiaSaigon: + return true + case AsiaSakhalin: + return true + case AsiaSamarkand: + return true + case AsiaSeoul: + return true + case AsiaShanghai: + return true + case AsiaSingapore: + return true + case AsiaSrednekolymsk: + return true + case AsiaTaipei: + return true + case AsiaTashkent: + return true + case AsiaTbilisi: + return true + case AsiaTehran: + return true + case AsiaTelAviv: + return true + case AsiaThimbu: + return true + case AsiaThimphu: + return true + case AsiaTokyo: + return true + case AsiaTomsk: + return true + case AsiaUjungPandang: + return true + case AsiaUlaanbaatar: + return true + case AsiaUlanBator: + return true + case AsiaUrumqi: + return true + case AsiaUstNera: + return true + case AsiaVientiane: + return true + case AsiaVladivostok: + return true + case AsiaYakutsk: + return true + case AsiaYangon: + return true + case AsiaYekaterinburg: + return true + case AsiaYerevan: + return true + case AtlanticAzores: + return true + case AtlanticBermuda: + return true + case AtlanticCanary: + return true + case AtlanticCapeVerde: + return true + case AtlanticFaeroe: + return true + case AtlanticFaroe: + return true + case AtlanticJanMayen: + return true + case AtlanticMadeira: + return true + case AtlanticReykjavik: + return true + case AtlanticSouthGeorgia: + return true + case AtlanticStHelena: + return true + case AtlanticStanley: + return true + case AustraliaACT: + return true + case AustraliaAdelaide: + return true + case AustraliaBrisbane: + return true + case AustraliaBrokenHill: + return true + case AustraliaCanberra: + return true + case AustraliaCurrie: + return true + case AustraliaDarwin: + return true + case AustraliaEucla: + return true + case AustraliaHobart: + return true + case AustraliaLHI: + return true + case AustraliaLindeman: + return true + case AustraliaLordHowe: + return true + case AustraliaMelbourne: + return true + case AustraliaNSW: + return true + case AustraliaNorth: + return true + case AustraliaPerth: + return true + case AustraliaQueensland: + return true + case AustraliaSouth: + return true + case AustraliaSydney: + return true + case AustraliaTasmania: + return true + case AustraliaVictoria: + return true + case AustraliaWest: + return true + case AustraliaYancowinna: + return true + case BrazilAcre: + return true + case BrazilDeNoronha: + return true + case BrazilEast: + return true + case BrazilWest: + return true + case CET: + return true + case CST6CDT: + return true + case CanadaAtlantic: + return true + case CanadaCentral: + return true + case CanadaEastern: + return true + case CanadaMountain: + return true + case CanadaNewfoundland: + return true + case CanadaPacific: + return true + case CanadaSaskatchewan: + return true + case CanadaYukon: + return true + case ChileContinental: + return true + case ChileEasterIsland: + return true + case Cuba: + return true + case EET: + return true + case EST: + return true + case EST5EDT: + return true + case Egypt: + return true + case Eire: + return true + case EtcGMT: + return true + case EtcGMT0: + return true + case EtcGMT01: + return true + case EtcGMT02: + return true + case EtcGMT1: + return true + case EtcGMT10: + return true + case EtcGMT101: + return true + case EtcGMT11: + return true + case EtcGMT111: + return true + case EtcGMT12: + return true + case EtcGMT121: + return true + case EtcGMT13: + return true + case EtcGMT14: + return true + case EtcGMT2: + return true + case EtcGMT21: + return true + case EtcGMT3: + return true + case EtcGMT31: + return true + case EtcGMT4: + return true + case EtcGMT41: + return true + case EtcGMT5: + return true + case EtcGMT51: + return true + case EtcGMT6: + return true + case EtcGMT61: + return true + case EtcGMT7: + return true + case EtcGMT71: + return true + case EtcGMT8: + return true + case EtcGMT81: + return true + case EtcGMT9: + return true + case EtcGMT91: + return true + case EtcGreenwich: + return true + case EtcUCT: + return true + case EtcUTC: + return true + case EtcUniversal: + return true + case EtcZulu: + return true + case EuropeAmsterdam: + return true + case EuropeAndorra: + return true + case EuropeAstrakhan: + return true + case EuropeAthens: + return true + case EuropeBelfast: + return true + case EuropeBelgrade: + return true + case EuropeBerlin: + return true + case EuropeBratislava: + return true + case EuropeBrussels: + return true + case EuropeBucharest: + return true + case EuropeBudapest: + return true + case EuropeBusingen: + return true + case EuropeChisinau: + return true + case EuropeCopenhagen: + return true + case EuropeDublin: + return true + case EuropeGibraltar: + return true + case EuropeGuernsey: + return true + case EuropeHelsinki: + return true + case EuropeIsleOfMan: + return true + case EuropeIstanbul: + return true + case EuropeJersey: + return true + case EuropeKaliningrad: + return true + case EuropeKiev: + return true + case EuropeKirov: + return true + case EuropeKyiv: + return true + case EuropeLisbon: + return true + case EuropeLjubljana: + return true + case EuropeLondon: + return true + case EuropeLuxembourg: + return true + case EuropeMadrid: + return true + case EuropeMalta: + return true + case EuropeMariehamn: + return true + case EuropeMinsk: + return true + case EuropeMonaco: + return true + case EuropeMoscow: + return true + case EuropeNicosia: + return true + case EuropeOslo: + return true + case EuropeParis: + return true + case EuropePodgorica: + return true + case EuropePrague: + return true + case EuropeRiga: + return true + case EuropeRome: + return true + case EuropeSamara: + return true + case EuropeSanMarino: + return true + case EuropeSarajevo: + return true + case EuropeSaratov: + return true + case EuropeSimferopol: + return true + case EuropeSkopje: + return true + case EuropeSofia: + return true + case EuropeStockholm: + return true + case EuropeTallinn: + return true + case EuropeTirane: + return true + case EuropeTiraspol: + return true + case EuropeUlyanovsk: + return true + case EuropeUzhgorod: + return true + case EuropeVaduz: + return true + case EuropeVatican: + return true + case EuropeVienna: + return true + case EuropeVilnius: + return true + case EuropeVolgograd: + return true + case EuropeWarsaw: + return true + case EuropeZagreb: + return true + case EuropeZaporozhye: + return true + case EuropeZurich: + return true + case GB: + return true + case GBEire: + return true + case GMT: + return true + case GMT0: + return true + case GMT01: + return true + case GMT02: + return true + case Greenwich: + return true + case HST: + return true + case Hongkong: + return true + case Iceland: + return true + case IndianAntananarivo: + return true + case IndianChagos: + return true + case IndianChristmas: + return true + case IndianCocos: + return true + case IndianComoro: + return true + case IndianKerguelen: + return true + case IndianMahe: + return true + case IndianMaldives: + return true + case IndianMauritius: + return true + case IndianMayotte: + return true + case IndianReunion: + return true + case Iran: + return true + case Israel: + return true + case Jamaica: + return true + case Japan: + return true + case Kwajalein: + return true + case Libya: + return true + case MET: + return true + case MST: + return true + case MST7MDT: + return true + case MexicoBajaNorte: + return true + case MexicoBajaSur: + return true + case MexicoGeneral: + return true + case NZ: + return true + case NZCHAT: + return true + case Navajo: + return true + case PRC: + return true + case PST8PDT: + return true + case PacificApia: + return true + case PacificAuckland: + return true + case PacificBougainville: + return true + case PacificChatham: + return true + case PacificChuuk: + return true + case PacificEaster: + return true + case PacificEfate: + return true + case PacificEnderbury: + return true + case PacificFakaofo: + return true + case PacificFiji: + return true + case PacificFunafuti: + return true + case PacificGalapagos: + return true + case PacificGambier: + return true + case PacificGuadalcanal: + return true + case PacificGuam: + return true + case PacificHonolulu: + return true + case PacificJohnston: + return true + case PacificKanton: + return true + case PacificKiritimati: + return true + case PacificKosrae: + return true + case PacificKwajalein: + return true + case PacificMajuro: + return true + case PacificMarquesas: + return true + case PacificMidway: + return true + case PacificNauru: + return true + case PacificNiue: + return true + case PacificNorfolk: + return true + case PacificNoumea: + return true + case PacificPagoPago: + return true + case PacificPalau: + return true + case PacificPitcairn: + return true + case PacificPohnpei: + return true + case PacificPonape: + return true + case PacificPortMoresby: + return true + case PacificRarotonga: + return true + case PacificSaipan: + return true + case PacificSamoa: + return true + case PacificTahiti: + return true + case PacificTarawa: + return true + case PacificTongatapu: + return true + case PacificTruk: + return true + case PacificWake: + return true + case PacificWallis: + return true + case PacificYap: + return true + case Poland: + return true + case Portugal: + return true + case ROC: + return true + case ROK: + return true + case Singapore: + return true + case Turkey: + return true + case UCT: + return true + case USAlaska: + return true + case USAleutian: + return true + case USArizona: + return true + case USCentral: + return true + case USEastIndiana: + return true + case USEastern: + return true + case USHawaii: + return true + case USIndianaStarke: + return true + case USMichigan: + return true + case USMountain: + return true + case USPacific: + return true + case USSamoa: + return true + case UTC: + return true + case Universal: + return true + case WET: + return true + case WSU: + return true + case Zulu: + return true + default: + return false + } +} + // Defines values for DataSourceV1State. const ( Connected DataSourceV1State = "connected" @@ -644,6 +1884,24 @@ const ( PendingReconnect DataSourceV1State = "pendingReconnect" ) +// Valid indicates whether the value is a known member of the DataSourceV1State enum. +func (e DataSourceV1State) Valid() bool { + switch e { + case Connected: + return true + case Disconnected: + return true + case Error: + return true + case Pending: + return true + case PendingReconnect: + return true + default: + return false + } +} + // Defines values for DiagnosisTypeV1. const ( DiagnosisTypeV1Empty DiagnosisTypeV1 = "" @@ -658,16 +1916,64 @@ const ( DiagnosisTypeV1Type3c DiagnosisTypeV1 = "type3c" ) +// Valid indicates whether the value is a known member of the DiagnosisTypeV1 enum. +func (e DiagnosisTypeV1) Valid() bool { + switch e { + case DiagnosisTypeV1Empty: + return true + case DiagnosisTypeV1Gestational: + return true + case DiagnosisTypeV1Lada: + return true + case DiagnosisTypeV1Mody: + return true + case DiagnosisTypeV1NotApplicable: + return true + case DiagnosisTypeV1Other: + return true + case DiagnosisTypeV1Prediabetes: + return true + case DiagnosisTypeV1Type1: + return true + case DiagnosisTypeV1Type2: + return true + case DiagnosisTypeV1Type3c: + return true + default: + return false + } +} + // Defines values for EhrMatchMessageRefV1DataModel. const ( Order EhrMatchMessageRefV1DataModel = "Order" ) +// Valid indicates whether the value is a known member of the EhrMatchMessageRefV1DataModel enum. +func (e EhrMatchMessageRefV1DataModel) Valid() bool { + switch e { + case Order: + return true + default: + return false + } +} + // Defines values for EhrMatchMessageRefV1EventType. const ( EhrMatchMessageRefV1EventTypeNew EhrMatchMessageRefV1EventType = "New" ) +// Valid indicates whether the value is a known member of the EhrMatchMessageRefV1EventType enum. +func (e EhrMatchMessageRefV1EventType) Valid() bool { + switch e { + case EhrMatchMessageRefV1EventTypeNew: + return true + default: + return false + } +} + // Defines values for EhrMatchRequestPatientsOptionsV1Criteria. const ( DOBFULLNAME EhrMatchRequestPatientsOptionsV1Criteria = "DOB_FULLNAME" @@ -675,24 +1981,74 @@ const ( MRNDOB EhrMatchRequestPatientsOptionsV1Criteria = "MRN_DOB" ) +// Valid indicates whether the value is a known member of the EhrMatchRequestPatientsOptionsV1Criteria enum. +func (e EhrMatchRequestPatientsOptionsV1Criteria) Valid() bool { + switch e { + case DOBFULLNAME: + return true + case MRN: + return true + case MRNDOB: + return true + default: + return false + } +} + // Defines values for EhrMatchRequestPatientsOptionsV1OnUniqueMatch. const ( DISABLEREPORTS EhrMatchRequestPatientsOptionsV1OnUniqueMatch = "DISABLE_REPORTS" ENABLEREPORTS EhrMatchRequestPatientsOptionsV1OnUniqueMatch = "ENABLE_REPORTS" ) +// Valid indicates whether the value is a known member of the EhrMatchRequestPatientsOptionsV1OnUniqueMatch enum. +func (e EhrMatchRequestPatientsOptionsV1OnUniqueMatch) Valid() bool { + switch e { + case DISABLEREPORTS: + return true + case ENABLEREPORTS: + return true + default: + return false + } +} + // Defines values for EhrSettingsV1Provider. const ( Redox EhrSettingsV1Provider = "redox" Xealth EhrSettingsV1Provider = "xealth" ) +// Valid indicates whether the value is a known member of the EhrSettingsV1Provider enum. +func (e EhrSettingsV1Provider) Valid() bool { + switch e { + case Redox: + return true + case Xealth: + return true + default: + return false + } +} + // Defines values for GlycemicRangesV1Type. const ( Custom GlycemicRangesV1Type = "custom" Preset GlycemicRangesV1Type = "preset" ) +// Valid indicates whether the value is a known member of the GlycemicRangesV1Type enum. +func (e GlycemicRangesV1Type) Valid() bool { + switch e { + case Custom: + return true + case Preset: + return true + default: + return false + } +} + // Defines values for GlycemicRangesPresetV1. const ( ADAHighRisk GlycemicRangesPresetV1 = "adaHighRisk" @@ -701,12 +2057,40 @@ const ( ADAStandard GlycemicRangesPresetV1 = "adaStandard" ) +// Valid indicates whether the value is a known member of the GlycemicRangesPresetV1 enum. +func (e GlycemicRangesPresetV1) Valid() bool { + switch e { + case ADAHighRisk: + return true + case ADAPregnancyType1: + return true + case ADAPregnancyType2: + return true + case ADAStandard: + return true + default: + return false + } +} + // Defines values for GlycemicRangesThresholdUpperBoundV1Units. const ( GlycemicRangesThresholdUpperBoundV1UnitsMgdL GlycemicRangesThresholdUpperBoundV1Units = "mg/dL" GlycemicRangesThresholdUpperBoundV1UnitsMmolL GlycemicRangesThresholdUpperBoundV1Units = "mmol/L" ) +// Valid indicates whether the value is a known member of the GlycemicRangesThresholdUpperBoundV1Units enum. +func (e GlycemicRangesThresholdUpperBoundV1Units) Valid() bool { + switch e { + case GlycemicRangesThresholdUpperBoundV1UnitsMgdL: + return true + case GlycemicRangesThresholdUpperBoundV1UnitsMmolL: + return true + default: + return false + } +} + // Defines values for MigrationStatusV1. const ( COMPLETED MigrationStatusV1 = "COMPLETED" @@ -714,6 +2098,20 @@ const ( RUNNING MigrationStatusV1 = "RUNNING" ) +// Valid indicates whether the value is a known member of the MigrationStatusV1 enum. +func (e MigrationStatusV1) Valid() bool { + switch e { + case COMPLETED: + return true + case PENDING: + return true + case RUNNING: + return true + default: + return false + } +} + // Defines values for ProviderIdV1. const ( Abbott ProviderIdV1 = "abbott" @@ -721,6 +2119,20 @@ const ( Twiist ProviderIdV1 = "twiist" ) +// Valid indicates whether the value is a known member of the ProviderIdV1 enum. +func (e ProviderIdV1) Valid() bool { + switch e { + case Abbott: + return true + case Dexcom: + return true + case Twiist: + return true + default: + return false + } +} + // Defines values for ScheduledReportsV1Cadence. const ( DISABLED ScheduledReportsV1Cadence = "DISABLED" @@ -730,12 +2142,42 @@ const ( N7d ScheduledReportsV1Cadence = "7d" ) +// Valid indicates whether the value is a known member of the ScheduledReportsV1Cadence enum. +func (e ScheduledReportsV1Cadence) Valid() bool { + switch e { + case DISABLED: + return true + case N14d: + return true + case N1d: + return true + case N30d: + return true + case N7d: + return true + default: + return false + } +} + // Defines values for ScheduledReportsV1OnUploadNoteEventType. const ( ScheduledReportsV1OnUploadNoteEventTypeNew ScheduledReportsV1OnUploadNoteEventType = "New" ScheduledReportsV1OnUploadNoteEventTypeReplace ScheduledReportsV1OnUploadNoteEventType = "Replace" ) +// Valid indicates whether the value is a known member of the ScheduledReportsV1OnUploadNoteEventType enum. +func (e ScheduledReportsV1OnUploadNoteEventType) Valid() bool { + switch e { + case ScheduledReportsV1OnUploadNoteEventTypeNew: + return true + case ScheduledReportsV1OnUploadNoteEventTypeReplace: + return true + default: + return false + } +} + // Defines values for TierV1. const ( Tier0100 TierV1 = "tier0100" @@ -744,6 +2186,22 @@ const ( Tier0400 TierV1 = "tier0400" ) +// Valid indicates whether the value is a known member of the TierV1 enum. +func (e TierV1) Valid() bool { + switch e { + case Tier0100: + return true + case Tier0200: + return true + case Tier0300: + return true + case Tier0400: + return true + default: + return false + } +} + // Defines values for TideReportParamsCategories. const ( DropInTimeInTargetPercent TideReportParamsCategories = "dropInTimeInTargetPercent" @@ -757,16 +2215,54 @@ const ( TimeInVeryLowPercent TideReportParamsCategories = "timeInVeryLowPercent" ) +// Valid indicates whether the value is a known member of the TideReportParamsCategories enum. +func (e TideReportParamsCategories) Valid() bool { + switch e { + case DropInTimeInTargetPercent: + return true + case MeetingTargets: + return true + case TimeCGMUsePercent: + return true + case TimeInAnyHighPercent: + return true + case TimeInAnyLowPercent: + return true + case TimeInExtremeHighPercent: + return true + case TimeInTargetPercent: + return true + case TimeInVeryHighPercent: + return true + case TimeInVeryLowPercent: + return true + default: + return false + } +} + // Defines values for FindPatientsParamsWorkspaceIdType. const ( FindPatientsParamsWorkspaceIdTypeClinicId FindPatientsParamsWorkspaceIdType = "clinicId" FindPatientsParamsWorkspaceIdTypeEhrSourceId FindPatientsParamsWorkspaceIdType = "ehrSourceId" ) -// FloatFilter defines model for FloatFilter. +// Valid indicates whether the value is a known member of the FindPatientsParamsWorkspaceIdType enum. +func (e FindPatientsParamsWorkspaceIdType) Valid() bool { + switch e { + case FindPatientsParamsWorkspaceIdTypeClinicId: + return true + case FindPatientsParamsWorkspaceIdTypeEhrSourceId: + return true + default: + return false + } +} + +// FloatFilter Example: >=5.5 type FloatFilter = string -// IntFilter defines model for IntFilter. +// IntFilter Example: >=7 type IntFilter = string // UpdateSuppressedNotifications defines model for UpdateSuppressedNotifications. @@ -776,6 +2272,7 @@ type UpdateSuppressedNotifications struct { // UpdateTier defines model for UpdateTier. type UpdateTier struct { + // Tier Example: tier0100 Tier TierV1 `json:"tier"` } @@ -794,146 +2291,242 @@ type AssociateClinicianToUserV1 struct { // BgmPeriodV1 Summary of a specific BGM time period (currently: 1d, 7d, 14d, 30d) type BgmPeriodV1 struct { // AverageDailyRecords Average daily readings + // + // Example: 3.5 AverageDailyRecords *float64 `json:"averageDailyRecords,omitempty"` // AverageDailyRecordsDelta Difference between the averageDailyRecords in this period and version in the opposite offset + // + // Example: 2.5 AverageDailyRecordsDelta *float64 `json:"averageDailyRecordsDelta,omitempty"` // AverageGlucoseMmol Average Glucose of records in this period + // + // Example: 5.5 AverageGlucoseMmol *float64 `json:"averageGlucoseMmol,omitempty"` // AverageGlucoseMmolDelta Difference between the averageGlucose in this period and the other offset version - AverageGlucoseMmolDelta *float64 `json:"averageGlucoseMmolDelta,omitempty"` - CoefficientOfVariation *float64 `json:"coefficientOfVariation,omitempty"` + // + // Example: 2.5 + AverageGlucoseMmolDelta *float64 `json:"averageGlucoseMmolDelta,omitempty"` + + // CoefficientOfVariation Example: 2.5 + CoefficientOfVariation *float64 `json:"coefficientOfVariation,omitempty"` + + // CoefficientOfVariationDelta Example: 2.5 CoefficientOfVariationDelta *float64 `json:"coefficientOfVariationDelta,omitempty"` - DaysWithData int `json:"daysWithData"` - DaysWithDataDelta int `json:"daysWithDataDelta"` - HasAverageDailyRecords bool `json:"hasAverageDailyRecords"` - HasAverageGlucoseMmol bool `json:"hasAverageGlucoseMmol"` - HasTimeInAnyHighPercent bool `json:"hasTimeInAnyHighPercent"` - HasTimeInAnyHighRecords bool `json:"hasTimeInAnyHighRecords"` - HasTimeInAnyLowPercent bool `json:"hasTimeInAnyLowPercent"` - HasTimeInAnyLowRecords bool `json:"hasTimeInAnyLowRecords"` - HasTimeInExtremeHighPercent bool `json:"hasTimeInExtremeHighPercent"` - HasTimeInExtremeHighRecords bool `json:"hasTimeInExtremeHighRecords"` - HasTimeInHighPercent bool `json:"hasTimeInHighPercent"` - HasTimeInHighRecords bool `json:"hasTimeInHighRecords"` - HasTimeInLowPercent bool `json:"hasTimeInLowPercent"` - HasTimeInLowRecords bool `json:"hasTimeInLowRecords"` - HasTimeInTargetPercent bool `json:"hasTimeInTargetPercent"` - HasTimeInTargetRecords bool `json:"hasTimeInTargetRecords"` - HasTimeInVeryHighPercent bool `json:"hasTimeInVeryHighPercent"` - HasTimeInVeryHighRecords bool `json:"hasTimeInVeryHighRecords"` - HasTimeInVeryLowPercent bool `json:"hasTimeInVeryLowPercent"` - HasTimeInVeryLowRecords bool `json:"hasTimeInVeryLowRecords"` - HasTotalRecords bool `json:"hasTotalRecords"` - Max float64 `json:"max"` - MaxDelta float64 `json:"maxDelta"` - Min float64 `json:"min"` - MinDelta float64 `json:"minDelta"` - StandardDeviation *float64 `json:"standardDeviation,omitempty"` - StandardDeviationDelta *float64 `json:"standardDeviationDelta,omitempty"` + + // DaysWithData Example: 2 + DaysWithData int `json:"daysWithData"` + + // DaysWithDataDelta Example: 2 + DaysWithDataDelta int `json:"daysWithDataDelta"` + HasAverageDailyRecords bool `json:"hasAverageDailyRecords"` + HasAverageGlucoseMmol bool `json:"hasAverageGlucoseMmol"` + HasTimeInAnyHighPercent bool `json:"hasTimeInAnyHighPercent"` + HasTimeInAnyHighRecords bool `json:"hasTimeInAnyHighRecords"` + HasTimeInAnyLowPercent bool `json:"hasTimeInAnyLowPercent"` + HasTimeInAnyLowRecords bool `json:"hasTimeInAnyLowRecords"` + HasTimeInExtremeHighPercent bool `json:"hasTimeInExtremeHighPercent"` + HasTimeInExtremeHighRecords bool `json:"hasTimeInExtremeHighRecords"` + HasTimeInHighPercent bool `json:"hasTimeInHighPercent"` + HasTimeInHighRecords bool `json:"hasTimeInHighRecords"` + HasTimeInLowPercent bool `json:"hasTimeInLowPercent"` + HasTimeInLowRecords bool `json:"hasTimeInLowRecords"` + HasTimeInTargetPercent bool `json:"hasTimeInTargetPercent"` + HasTimeInTargetRecords bool `json:"hasTimeInTargetRecords"` + HasTimeInVeryHighPercent bool `json:"hasTimeInVeryHighPercent"` + HasTimeInVeryHighRecords bool `json:"hasTimeInVeryHighRecords"` + HasTimeInVeryLowPercent bool `json:"hasTimeInVeryLowPercent"` + HasTimeInVeryLowRecords bool `json:"hasTimeInVeryLowRecords"` + HasTotalRecords bool `json:"hasTotalRecords"` + + // Max Example: 8 + Max float64 `json:"max"` + + // MaxDelta Example: 8 + MaxDelta float64 `json:"maxDelta"` + + // Min Example: 3 + Min float64 `json:"min"` + + // MinDelta Example: 3 + MinDelta float64 `json:"minDelta"` + + // StandardDeviation Example: 2.5 + StandardDeviation *float64 `json:"standardDeviation,omitempty"` + + // StandardDeviationDelta Example: 2.5 + StandardDeviationDelta *float64 `json:"standardDeviationDelta,omitempty"` // TimeInAnyHighPercent Percentage of time spent in Any high glucose range + // + // Example: 0.35 TimeInAnyHighPercent *float64 `json:"timeInAnyHighPercent,omitempty"` // TimeInAnyHighPercentDelta Difference between the timeInAnyHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInAnyHighPercentDelta *float64 `json:"timeInAnyHighPercentDelta,omitempty"` // TimeInAnyHighRecords Counter of records in Any high glucose range + // + // Example: 10 TimeInAnyHighRecords *int `json:"timeInAnyHighRecords,omitempty"` // TimeInAnyHighRecordsDelta Difference between the timeInAnyHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInAnyHighRecordsDelta *int `json:"timeInAnyHighRecordsDelta,omitempty"` // TimeInAnyLowPercent Percentage of time spent in Any low glucose range + // + // Example: 0.35 TimeInAnyLowPercent *float64 `json:"timeInAnyLowPercent,omitempty"` // TimeInAnyLowPercentDelta Difference between the timeInAnyLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInAnyLowPercentDelta *float64 `json:"timeInAnyLowPercentDelta,omitempty"` // TimeInAnyLowRecords Counter of records in Any low glucose range + // + // Example: 10 TimeInAnyLowRecords *int `json:"timeInAnyLowRecords,omitempty"` // TimeInAnyLowRecordsDelta Difference between the timeInAnyLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInAnyLowRecordsDelta *int `json:"timeInAnyLowRecordsDelta,omitempty"` // TimeInExtremeHighPercent Percentage of time spent in extreme high glucose range + // + // Example: 0.35 TimeInExtremeHighPercent *float64 `json:"timeInExtremeHighPercent,omitempty"` // TimeInExtremeHighPercentDelta Difference between the timeInExtremeHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInExtremeHighPercentDelta *float64 `json:"timeInExtremeHighPercentDelta,omitempty"` // TimeInExtremeHighRecords Counter of records in extreme high glucose range + // + // Example: 10 TimeInExtremeHighRecords *int `json:"timeInExtremeHighRecords,omitempty"` // TimeInExtremeHighRecordsDelta Difference between the timeInExtremeHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInExtremeHighRecordsDelta *int `json:"timeInExtremeHighRecordsDelta,omitempty"` // TimeInHighPercent Percentage of time spent in high glucose range + // + // Example: 0.35 TimeInHighPercent *float64 `json:"timeInHighPercent,omitempty"` // TimeInHighPercentDelta Difference between the timeInHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInHighPercentDelta *float64 `json:"timeInHighPercentDelta,omitempty"` // TimeInHighRecords Counter of records in high glucose range + // + // Example: 10 TimeInHighRecords *int `json:"timeInHighRecords,omitempty"` // TimeInHighRecordsDelta Difference between the timeInHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInHighRecordsDelta *int `json:"timeInHighRecordsDelta,omitempty"` // TimeInLowPercent Percentage of time spent in low glucose range + // + // Example: 0.35 TimeInLowPercent *float64 `json:"timeInLowPercent,omitempty"` // TimeInLowPercentDelta Difference between the timeInLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInLowPercentDelta *float64 `json:"timeInLowPercentDelta,omitempty"` // TimeInLowRecords Counter of records in low glucose range + // + // Example: 10 TimeInLowRecords *int `json:"timeInLowRecords,omitempty"` // TimeInLowRecordsDelta Difference between the timeInLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInLowRecordsDelta *int `json:"timeInLowRecordsDelta,omitempty"` // TimeInTargetPercent Percentage of time spent in target glucose range + // + // Example: 0.35 TimeInTargetPercent *float64 `json:"timeInTargetPercent,omitempty"` // TimeInTargetPercentDelta Difference between the timeInTargetPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInTargetPercentDelta *float64 `json:"timeInTargetPercentDelta,omitempty"` // TimeInTargetRecords Counter of records in target glucose range + // + // Example: 10 TimeInTargetRecords *int `json:"timeInTargetRecords,omitempty"` // TimeInTargetRecordsDelta Difference between the timeInTargetRecords in this period and version in the opposite offset + // + // Example: 5 TimeInTargetRecordsDelta *int `json:"timeInTargetRecordsDelta,omitempty"` // TimeInVeryHighPercent Percentage of time spent in very high glucose range + // + // Example: 0.35 TimeInVeryHighPercent *float64 `json:"timeInVeryHighPercent,omitempty"` // TimeInVeryHighPercentDelta Difference between the timeInVeryHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInVeryHighPercentDelta *float64 `json:"timeInVeryHighPercentDelta,omitempty"` // TimeInVeryHighRecords Counter of records in very high glucose range + // + // Example: 10 TimeInVeryHighRecords *int `json:"timeInVeryHighRecords,omitempty"` // TimeInVeryHighRecordsDelta Difference between the timeInVeryHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInVeryHighRecordsDelta *int `json:"timeInVeryHighRecordsDelta,omitempty"` // TimeInVeryLowPercent Percentage of time spent in very low glucose range + // + // Example: 0.35 TimeInVeryLowPercent *float64 `json:"timeInVeryLowPercent,omitempty"` // TimeInVeryLowPercentDelta Difference between the timeInVeryLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInVeryLowPercentDelta *float64 `json:"timeInVeryLowPercentDelta,omitempty"` // TimeInVeryLowRecords Counter of records in very low glucose range + // + // Example: 10 TimeInVeryLowRecords *int `json:"timeInVeryLowRecords,omitempty"` // TimeInVeryLowRecordsDelta Difference between the timeInVeryLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInVeryLowRecordsDelta *int `json:"timeInVeryLowRecordsDelta,omitempty"` // TotalRecords Counter of records + // + // Example: 10 TotalRecords *int `json:"totalRecords,omitempty"` // TotalRecordsDelta Difference between the totalRecords in this period and version in the opposite offset + // + // Example: 10 TotalRecordsDelta *int `json:"totalRecordsDelta,omitempty"` } @@ -949,6 +2542,8 @@ type BgmStatsV1 struct { Dates SummaryDatesV1 `json:"dates,omitempty"` // Id Summary Unique Identifier + // + // Example: 2fe2488217ee43e1b2e83c2f Id *SummaryIdV1 `json:"id,omitempty"` // Periods A map to each supported BGM summary period @@ -958,25 +2553,45 @@ type BgmStatsV1 struct { // CgmPeriodV1 Summary of a specific CGM time period (currently: 1d, 7d, 14d, 30d) type CgmPeriodV1 struct { // AverageDailyRecords Average daily readings + // + // Example: 3.5 AverageDailyRecords *float64 `json:"averageDailyRecords,omitempty"` // AverageDailyRecordsDelta Difference between the averageDailyRecords in this period and version in the opposite offset + // + // Example: 2.5 AverageDailyRecordsDelta *float64 `json:"averageDailyRecordsDelta,omitempty"` // AverageGlucoseMmol Average Glucose of records in this period + // + // Example: 5.5 AverageGlucoseMmol *float64 `json:"averageGlucoseMmol,omitempty"` // AverageGlucoseMmolDelta Difference between the averageGlucose in this period and the other offset version - AverageGlucoseMmolDelta *float64 `json:"averageGlucoseMmolDelta,omitempty"` - CoefficientOfVariation float64 `json:"coefficientOfVariation"` - CoefficientOfVariationDelta float64 `json:"coefficientOfVariationDelta"` - DaysWithData int `json:"daysWithData"` - DaysWithDataDelta int `json:"daysWithDataDelta"` + // + // Example: 2.5 + AverageGlucoseMmolDelta *float64 `json:"averageGlucoseMmolDelta,omitempty"` + + // CoefficientOfVariation Example: 2.5 + CoefficientOfVariation float64 `json:"coefficientOfVariation"` + + // CoefficientOfVariationDelta Example: 2.5 + CoefficientOfVariationDelta float64 `json:"coefficientOfVariationDelta"` + + // DaysWithData Example: 2 + DaysWithData int `json:"daysWithData"` + + // DaysWithDataDelta Example: 2 + DaysWithDataDelta int `json:"daysWithDataDelta"` // GlucoseManagementIndicator A derived value which emulates A1C + // + // Example: 7.5 GlucoseManagementIndicator *float64 `json:"glucoseManagementIndicator,omitempty"` // GlucoseManagementIndicatorDelta Difference between the glucoseManagementIndicator in this period and the other offset version + // + // Example: 7.5 GlucoseManagementIndicatorDelta *float64 `json:"glucoseManagementIndicatorDelta,omitempty"` HasAverageDailyRecords bool `json:"hasAverageDailyRecords"` HasAverageGlucoseMmol bool `json:"hasAverageGlucoseMmol"` @@ -1009,181 +2624,309 @@ type CgmPeriodV1 struct { HasTimeInVeryLowPercent bool `json:"hasTimeInVeryLowPercent"` HasTimeInVeryLowRecords bool `json:"hasTimeInVeryLowRecords"` HasTotalRecords bool `json:"hasTotalRecords"` - HoursWithData int `json:"hoursWithData"` - HoursWithDataDelta int `json:"hoursWithDataDelta"` - Max float64 `json:"max"` - MaxDelta float64 `json:"maxDelta"` - Min float64 `json:"min"` - MinDelta float64 `json:"minDelta"` - StandardDeviation float64 `json:"standardDeviation"` - StandardDeviationDelta float64 `json:"standardDeviationDelta"` + + // HoursWithData Example: 2 + HoursWithData int `json:"hoursWithData"` + + // HoursWithDataDelta Example: 2 + HoursWithDataDelta int `json:"hoursWithDataDelta"` + + // Max Example: 8 + Max float64 `json:"max"` + + // MaxDelta Example: 8 + MaxDelta float64 `json:"maxDelta"` + + // Min Example: 3 + Min float64 `json:"min"` + + // MinDelta Example: 3 + MinDelta float64 `json:"minDelta"` + + // StandardDeviation Example: 2.5 + StandardDeviation float64 `json:"standardDeviation"` + + // StandardDeviationDelta Example: 2.5 + StandardDeviationDelta float64 `json:"standardDeviationDelta"` // TimeCGMUseMinutes Counter of minutes spent wearing a cgm + // + // Example: 200 TimeCGMUseMinutes *int `json:"timeCGMUseMinutes,omitempty"` // TimeCGMUseMinutesDelta Difference between the timeCGMUseMinutes in this period and version in the opposite offset + // + // Example: 5 TimeCGMUseMinutesDelta *int `json:"timeCGMUseMinutesDelta,omitempty"` // TimeCGMUsePercent Percentage of time spent wearing a cgm + // + // Example: 0.35 TimeCGMUsePercent *float64 `json:"timeCGMUsePercent,omitempty"` // TimeCGMUsePercentDelta Difference between the timeCGMUsePercent in this period and version in the opposite offset + // + // Example: 0.2 TimeCGMUsePercentDelta *float64 `json:"timeCGMUsePercentDelta,omitempty"` // TimeCGMUseRecords Counter of minutes spent wearing a cgm + // + // Example: 10 TimeCGMUseRecords *int `json:"timeCGMUseRecords,omitempty"` // TimeCGMUseRecordsDelta Difference between the timeCGMUseRecords in this period and version in the opposite offset + // + // Example: 5 TimeCGMUseRecordsDelta *int `json:"timeCGMUseRecordsDelta,omitempty"` // TimeInAnyHighMinutes Counter of minutes spent in Any high glucose range + // + // Example: 200 TimeInAnyHighMinutes *int `json:"timeInAnyHighMinutes,omitempty"` // TimeInAnyHighMinutesDelta Difference between the timeInAnyHighMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInAnyHighMinutesDelta *int `json:"timeInAnyHighMinutesDelta,omitempty"` // TimeInAnyHighPercent Percentage of time spent in Any high glucose range + // + // Example: 0.35 TimeInAnyHighPercent *float64 `json:"timeInAnyHighPercent,omitempty"` // TimeInAnyHighPercentDelta Difference between the timeInAnyHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInAnyHighPercentDelta *float64 `json:"timeInAnyHighPercentDelta,omitempty"` // TimeInAnyHighRecords Counter of records in Any high glucose range + // + // Example: 10 TimeInAnyHighRecords *int `json:"timeInAnyHighRecords,omitempty"` // TimeInAnyHighRecordsDelta Difference between the timeInAnyHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInAnyHighRecordsDelta *int `json:"timeInAnyHighRecordsDelta,omitempty"` // TimeInAnyLowMinutes Counter of minutes spent in Any low glucose range + // + // Example: 200 TimeInAnyLowMinutes *int `json:"timeInAnyLowMinutes,omitempty"` // TimeInAnyLowMinutesDelta Difference between the timeInAnyLowMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInAnyLowMinutesDelta *int `json:"timeInAnyLowMinutesDelta,omitempty"` // TimeInAnyLowPercent Percentage of time spent in Any low glucose range + // + // Example: 0.35 TimeInAnyLowPercent *float64 `json:"timeInAnyLowPercent,omitempty"` // TimeInAnyLowPercentDelta Difference between the timeInAnyLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInAnyLowPercentDelta *float64 `json:"timeInAnyLowPercentDelta,omitempty"` // TimeInAnyLowRecords Counter of records in Any low glucose range + // + // Example: 10 TimeInAnyLowRecords *int `json:"timeInAnyLowRecords,omitempty"` // TimeInAnyLowRecordsDelta Difference between the timeInAnyLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInAnyLowRecordsDelta *int `json:"timeInAnyLowRecordsDelta,omitempty"` // TimeInExtremeHighMinutes Counter of minutes spent in extreme high glucose range + // + // Example: 200 TimeInExtremeHighMinutes *int `json:"timeInExtremeHighMinutes,omitempty"` // TimeInExtremeHighMinutesDelta Difference between the timeInExtremeHighMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInExtremeHighMinutesDelta *int `json:"timeInExtremeHighMinutesDelta,omitempty"` // TimeInExtremeHighPercent Percentage of time spent in extreme high glucose range + // + // Example: 0.35 TimeInExtremeHighPercent *float64 `json:"timeInExtremeHighPercent,omitempty"` // TimeInExtremeHighPercentDelta Difference between the timeInExtremeHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInExtremeHighPercentDelta *float64 `json:"timeInExtremeHighPercentDelta,omitempty"` // TimeInExtremeHighRecords Counter of records in extreme high glucose range + // + // Example: 10 TimeInExtremeHighRecords *int `json:"timeInExtremeHighRecords,omitempty"` // TimeInExtremeHighRecordsDelta Difference between the timeInExtremeHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInExtremeHighRecordsDelta *int `json:"timeInExtremeHighRecordsDelta,omitempty"` // TimeInHighMinutes Counter of minutes spent in high glucose range + // + // Example: 200 TimeInHighMinutes *int `json:"timeInHighMinutes,omitempty"` // TimeInHighMinutesDelta Difference between the timeInHighMinutes in this period and version in the opposite offset + // + // Example: 20 TimeInHighMinutesDelta *int `json:"timeInHighMinutesDelta,omitempty"` // TimeInHighPercent Percentage of time spent in high glucose range + // + // Example: 0.35 TimeInHighPercent *float64 `json:"timeInHighPercent,omitempty"` // TimeInHighPercentDelta Difference between the timeInHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInHighPercentDelta *float64 `json:"timeInHighPercentDelta,omitempty"` // TimeInHighRecords Counter of records in high glucose range + // + // Example: 10 TimeInHighRecords *int `json:"timeInHighRecords,omitempty"` // TimeInHighRecordsDelta Difference between the timeInHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInHighRecordsDelta *int `json:"timeInHighRecordsDelta,omitempty"` // TimeInLowMinutes Counter of minutes spent in low glucose range + // + // Example: 200 TimeInLowMinutes *int `json:"timeInLowMinutes,omitempty"` // TimeInLowMinutesDelta Difference between the timeInLowMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInLowMinutesDelta *int `json:"timeInLowMinutesDelta,omitempty"` // TimeInLowPercent Percentage of time spent in low glucose range + // + // Example: 0.35 TimeInLowPercent *float64 `json:"timeInLowPercent,omitempty"` // TimeInLowPercentDelta Difference between the timeInLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInLowPercentDelta *float64 `json:"timeInLowPercentDelta,omitempty"` // TimeInLowRecords Counter of records in low glucose range + // + // Example: 10 TimeInLowRecords *int `json:"timeInLowRecords,omitempty"` // TimeInLowRecordsDelta Difference between the timeInLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInLowRecordsDelta *int `json:"timeInLowRecordsDelta,omitempty"` // TimeInTargetMinutes Counter of minutes spent in target glucose range + // + // Example: 200 TimeInTargetMinutes *int `json:"timeInTargetMinutes,omitempty"` // TimeInTargetMinutesDelta Difference between the timeInTargetMinutes in this period and version in the opposite offset + // + // Example: 20 TimeInTargetMinutesDelta *int `json:"timeInTargetMinutesDelta,omitempty"` // TimeInTargetPercent Percentage of time spent in target glucose range + // + // Example: 0.35 TimeInTargetPercent *float64 `json:"timeInTargetPercent,omitempty"` // TimeInTargetPercentDelta Difference between the timeInTargetPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInTargetPercentDelta *float64 `json:"timeInTargetPercentDelta,omitempty"` // TimeInTargetRecords Counter of records in target glucose range + // + // Example: 10 TimeInTargetRecords *int `json:"timeInTargetRecords,omitempty"` // TimeInTargetRecordsDelta Difference between the timeInTargetRecords in this period and version in the opposite offset + // + // Example: 5 TimeInTargetRecordsDelta *int `json:"timeInTargetRecordsDelta,omitempty"` // TimeInVeryHighMinutes Counter of minutes spent in very high glucose range + // + // Example: 200 TimeInVeryHighMinutes *int `json:"timeInVeryHighMinutes,omitempty"` // TimeInVeryHighMinutesDelta Difference between the timeInVeryHighMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInVeryHighMinutesDelta *int `json:"timeInVeryHighMinutesDelta,omitempty"` // TimeInVeryHighPercent Percentage of time spent in very high glucose range + // + // Example: 0.35 TimeInVeryHighPercent *float64 `json:"timeInVeryHighPercent,omitempty"` // TimeInVeryHighPercentDelta Difference between the timeInVeryHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInVeryHighPercentDelta *float64 `json:"timeInVeryHighPercentDelta,omitempty"` // TimeInVeryHighRecords Counter of records in very high glucose range + // + // Example: 10 TimeInVeryHighRecords *int `json:"timeInVeryHighRecords,omitempty"` // TimeInVeryHighRecordsDelta Difference between the timeInVeryHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInVeryHighRecordsDelta *int `json:"timeInVeryHighRecordsDelta,omitempty"` // TimeInVeryLowMinutes Counter of minutes spent in very low glucose range + // + // Example: 200 TimeInVeryLowMinutes *int `json:"timeInVeryLowMinutes,omitempty"` // TimeInVeryLowMinutesDelta Difference between the timeInVeryLowMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInVeryLowMinutesDelta *int `json:"timeInVeryLowMinutesDelta,omitempty"` // TimeInVeryLowPercent Percentage of time spent in very low glucose range + // + // Example: 0.35 TimeInVeryLowPercent *float64 `json:"timeInVeryLowPercent,omitempty"` // TimeInVeryLowPercentDelta Difference between the timeInVeryLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInVeryLowPercentDelta *float64 `json:"timeInVeryLowPercentDelta,omitempty"` // TimeInVeryLowRecords Counter of records in very low glucose range + // + // Example: 10 TimeInVeryLowRecords *int `json:"timeInVeryLowRecords,omitempty"` // TimeInVeryLowRecordsDelta Difference between the timeInVeryLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInVeryLowRecordsDelta *int `json:"timeInVeryLowRecordsDelta,omitempty"` // TotalRecords Counter of records + // + // Example: 10 TotalRecords *int `json:"totalRecords,omitempty"` // TotalRecordsDelta Difference between the totalRecords in this period and version in the opposite offset + // + // Example: 10 TotalRecordsDelta *int `json:"totalRecordsDelta,omitempty"` } @@ -1199,6 +2942,8 @@ type CgmStatsV1 struct { Dates SummaryDatesV1 `json:"dates,omitempty"` // Id Summary Unique Identifier + // + // Example: 2fe2488217ee43e1b2e83c2f Id *SummaryIdV1 `json:"id,omitempty"` // Periods A map to each supported CGM summary period @@ -1206,6 +2951,8 @@ type CgmStatsV1 struct { } // CityV1 City name. +// +// Example: Palo Alto type CityV1 = string // ClinicV1 Clinic @@ -1215,15 +2962,21 @@ type ClinicV1 struct { CanMigrate *bool `json:"canMigrate,omitempty"` // City City name. + // + // Example: Palo Alto City *CityV1 `json:"city,omitempty"` ClinicSize *ClinicV1ClinicSize `json:"clinicSize,omitempty"` ClinicType *ClinicV1ClinicType `json:"clinicType,omitempty"` // Country Country name. + // + // Example: USA Country *CountryV1 `json:"country,omitempty"` CreatedTime *time.Time `json:"createdTime,omitempty"` // Id Clinic identifier. + // + // Example: 2fe2488217ee43e1b2e83c2f Id *ClinicIdV1 `json:"id,omitempty"` LastDeletedPatientTag *PatientTagV1 `json:"lastDeletedPatientTag,omitempty"` @@ -1235,6 +2988,8 @@ type ClinicV1 struct { PhoneNumbers *PhoneNumbersV1 `json:"phoneNumbers,omitempty"` // PostalCode Postal code. In the U.S., typically the zip code such as `94301` or `94301-1704`. + // + // Example: 94301 PostalCode *PostalCodeV1 `json:"postalCode,omitempty"` PreferredBgUnits ClinicV1PreferredBgUnits `json:"preferredBgUnits"` @@ -1243,13 +2998,19 @@ type ClinicV1 struct { Sites []SiteV1 `json:"sites,omitempty"` // State State or province. In the U.S., typically something like `CA` or `California`. + // + // Example: CA State *StateV1 `json:"state,omitempty"` SuppressedNotifications *SuppressedNotificationsV1 `json:"suppressedNotifications,omitempty"` - Tier *string `json:"tier,omitempty"` - TierDescription *string `json:"tierDescription,omitempty"` - Timezone *ClinicTimezoneV1 `json:"timezone,omitempty"` - UpdatedTime *time.Time `json:"updatedTime,omitempty"` - Website *string `json:"website,omitempty"` + + // Tier Example: tier0100 + Tier *string `json:"tier,omitempty"` + + // TierDescription Example: Free + TierDescription *string `json:"tierDescription,omitempty"` + Timezone *ClinicTimezoneV1 `json:"timezone,omitempty"` + UpdatedTime *time.Time `json:"updatedTime,omitempty"` + Website *string `json:"website,omitempty"` } // ClinicV1ClinicSize defines model for ClinicV1.ClinicSize. @@ -1262,6 +3023,8 @@ type ClinicV1ClinicType string type ClinicV1PreferredBgUnits string // ClinicIdV1 Clinic identifier. +// +// Example: 2fe2488217ee43e1b2e83c2f type ClinicIdV1 = string // ClinicTimezoneV1 defines model for clinicTimezone.v1. @@ -1306,13 +3069,19 @@ type CliniciansV1 = []ClinicianV1 type ClinicsV1 = []ClinicV1 // CountryV1 Country name. +// +// Example: USA type CountryV1 = string // CreatePatientV1 defines model for createPatient.v1. type CreatePatientV1 struct { - AttestationSubmitted *bool `json:"attestationSubmitted,omitempty"` - BirthDate *openapi_types.Date `json:"birthDate,omitempty"` - DiagnosisType *DiagnosisTypeV1 `json:"diagnosisType,omitempty"` + AttestationSubmitted *bool `json:"attestationSubmitted,omitempty"` + + // BirthDate Example: 2012-08-30 + BirthDate *openapi_types.Date `json:"birthDate,omitempty"` + + // DiagnosisType Example: type1 + DiagnosisType *DiagnosisTypeV1 `json:"diagnosisType,omitempty"` // FullName The full name of the patient FullName *string `json:"fullName,omitempty"` @@ -1326,7 +3095,7 @@ type CreatePatientV1 struct { Mrn *string `json:"mrn,omitempty"` Permissions *PatientPermissionsV1 `json:"permissions,omitempty"` Sites []SiteV1 `json:"sites,omitzero"` - Tags *PatientTagIdsV1 `json:"tags"` + Tags *PatientTagIdsV1 `json:"tags,omitempty"` } // DataSourceV1 defines model for dataSource.v1. @@ -1335,27 +3104,39 @@ type DataSourceV1 struct { DataSourceId *string `json:"dataSourceId,omitempty"` // ExpirationTime [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information + // + // Example: 2017-02-06T02:37:46Z ExpirationTime *DatetimeV1 `json:"expirationTime,omitempty"` // LatestDataTime [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information + // + // Example: 2017-02-06T02:37:46Z LatestDataTime *DatetimeV1 `json:"latestDataTime,omitempty"` // ModifiedTime [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information - ModifiedTime *DatetimeV1 `json:"modifiedTime,omitempty"` - ProviderName ProviderNameV1 `json:"providerName"` - State DataSourceV1State `json:"state"` + // + // Example: 2017-02-06T02:37:46Z + ModifiedTime *DatetimeV1 `json:"modifiedTime,omitempty"` + + // ProviderName Example: dexcom + ProviderName ProviderNameV1 `json:"providerName"` + + // State Example: connected + State DataSourceV1State `json:"state"` } -// DataSourceV1State defines model for DataSourceV1.State. +// DataSourceV1State Example: connected type DataSourceV1State string // DataSourcesV1 defines model for dataSources.v1. type DataSourcesV1 = []DataSourceV1 // DatetimeV1 [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information +// +// Example: 2017-02-06T02:37:46Z type DatetimeV1 = string -// DiagnosisTypeV1 defines model for diagnosisType.v1. +// DiagnosisTypeV1 Example: type1 type DiagnosisTypeV1 string // EhrDestinationsV1 defines model for ehrDestinations.v1. @@ -1468,13 +3249,18 @@ type EhrTagsSettingsV1 struct { // ErrorV1 defines model for error.v1. type ErrorV1 struct { - Code int `json:"code"` + // Code Example: 404 + Code int `json:"code"` + + // Message Example: Requested resources was not found Message string `json:"message"` } // GenerateMergeReportV1 defines model for generateMergeReport.v1. type GenerateMergeReportV1 struct { // SourceId Clinic identifier. + // + // Example: 2fe2488217ee43e1b2e83c2f SourceId *ClinicIdV1 `json:"sourceId,omitempty"` } @@ -1530,6 +3316,8 @@ type MembershipRestrictionsV1 struct { // MergeClinicV1 defines model for mergeClinic.v1. type MergeClinicV1 struct { // SourceId Clinic identifier. + // + // Example: 2fe2488217ee43e1b2e83c2f SourceId *ClinicIdV1 `json:"sourceId,omitempty"` } @@ -1582,18 +3370,25 @@ type NameV1 = string // ObjectIdV1 String representation of a resource id type ObjectIdV1 = string +// ObjectIdOrZeroV1 A resource id, or the underscore sentinel `_` meaning "records with none assigned". +type ObjectIdOrZeroV1 = string + // ObjectidV1 String representation of a resource id type ObjectidV1 = string // PatientV1 defines model for patient.v1. type PatientV1 struct { - AttestationSubmitted *bool `json:"attestationSubmitted,omitempty"` - BirthDate openapi_types.Date `json:"birthDate"` - ConnectionRequests *ProviderConnectionRequestsV1 `json:"connectionRequests,omitempty"` - CreatedTime *time.Time `json:"createdTime,omitempty"` - DataSources *[]DataSourceV1 `json:"dataSources"` - DiagnosisType *DiagnosisTypeV1 `json:"diagnosisType,omitempty"` - Email *string `json:"email,omitempty"` + AttestationSubmitted *bool `json:"attestationSubmitted,omitempty"` + + // BirthDate Example: 2012-08-30 + BirthDate openapi_types.Date `json:"birthDate"` + ConnectionRequests *ProviderConnectionRequestsV1 `json:"connectionRequests,omitempty"` + CreatedTime *time.Time `json:"createdTime,omitempty"` + DataSources *[]DataSourceV1 `json:"dataSources,omitempty"` + + // DiagnosisType Example: type1 + DiagnosisType *DiagnosisTypeV1 `json:"diagnosisType,omitempty"` + Email *string `json:"email,omitempty"` // FullName The full name of the patient FullName string `json:"fullName"` @@ -1606,14 +3401,22 @@ type PatientV1 struct { // Mrn The medical record number of the patient Mrn *string `json:"mrn,omitempty"` Permissions *PatientPermissionsV1 `json:"permissions,omitempty"` - Reviews []PatientReviewV1 `json:"reviews"` + Reviews []PatientReviewV1 `json:"reviews,omitempty"` Sites []SiteV1 `json:"sites,omitzero"` // Summary A summary of a patients recent data - Summary *PatientSummaryV1 `json:"summary,omitempty"` - Tags *PatientTagIdsV1 `json:"tags"` - TargetDevices *[]string `json:"targetDevices,omitempty"` - UpdatedTime *time.Time `json:"updatedTime,omitempty"` + Summary *PatientSummaryV1 `json:"summary,omitempty"` + Tags *PatientTagIdsV1 `json:"tags,omitempty"` + + // TargetDevices When uploading data, the user selects a target device (typically + // make and model) of their data source (i.e. meter or pump) from a + // list. This selection is used by the Tidepool Uploader to select the + // correct drivers for communicating with the data source. + // + // These values are stored in the user's profile so that they can be + // pre-selected on next use, thereby streamlining the process. + TargetDevices *TargetdevicesV1 `json:"targetDevices,omitempty"` + UpdatedTime *time.Time `json:"updatedTime,omitempty"` } // PatientClinicRelationshipV1 defines model for patientClinicRelationship.v1. @@ -1650,12 +3453,16 @@ type PatientCountV1 struct { // PatientCountLimitV1 defines model for patientCountLimit.v1. type PatientCountLimitV1 struct { // EndDate [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information + // + // Example: 2017-02-06T02:37:46Z EndDate *DatetimeV1 `json:"endDate,omitempty"` // Plan The plan-based patient count limit Plan int `json:"plan"` // StartDate [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information + // + // Example: 2017-02-06T02:37:46Z StartDate *DatetimeV1 `json:"startDate,omitempty"` } @@ -1724,7 +3531,7 @@ type PatientsResponseV1 struct { Meta *MetaV1 `json:"meta,omitempty"` } -// PhoneNumberV1 defines model for phoneNumber.v1. +// PhoneNumberV1 Example: {"number":"+1 (555) 555-1212","type":"main"} type PhoneNumberV1 struct { Number string `json:"number"` Type *string `json:"type,omitempty"` @@ -1734,6 +3541,8 @@ type PhoneNumberV1 struct { type PhoneNumbersV1 = []PhoneNumberV1 // PostalCodeV1 Postal code. In the U.S., typically the zip code such as `94301` or `94301-1704`. +// +// Example: 94301 type PostalCodeV1 = string // ProviderConnectionRequestV1 defines model for providerConnectionRequest.v1. @@ -1752,7 +3561,7 @@ type ProviderConnectionRequestsV1 struct { // ProviderIdV1 defines model for providerId.v1. type ProviderIdV1 string -// ProviderNameV1 defines model for providerName.v1. +// ProviderNameV1 Example: dexcom type ProviderNameV1 = string // ScheduledReportsV1 Scheduled Report Settings @@ -1798,10 +3607,15 @@ type SiteCreationV1 struct { // SiteIdV1 defines model for siteId.v1. type SiteIdV1 = string +// SiteIdsV1 defines model for siteIds.v1. +type SiteIdsV1 = []SiteIdV1 + // SiteNameV1 The site's name. type SiteNameV1 = string // StateV1 State or province. In the U.S., typically something like `CA` or `California`. +// +// Example: CA type StateV1 = string // StreetAddressV1 Street address. @@ -1857,6 +3671,8 @@ type SummaryDatesV1 struct { } // SummaryIdV1 Summary Unique Identifier +// +// Example: 2fe2488217ee43e1b2e83c2f type SummaryIdV1 = string // SuppressedNotificationsV1 defines model for suppressedNotifications.v1. @@ -1864,9 +3680,20 @@ type SuppressedNotificationsV1 struct { PatientClinicInvitation *bool `json:"patientClinicInvitation,omitempty"` } +// TargetdevicesV1 When uploading data, the user selects a target device (typically +// make and model) of their data source (i.e. meter or pump) from a +// list. This selection is used by the Tidepool Uploader to select the +// correct drivers for communicating with the data source. +// +// These values are stored in the user's profile so that they can be +// pre-selected on next use, thereby streamlining the process. +type TargetdevicesV1 = []string + // TideConfigV1 defines model for tideConfig.v1. type TideConfigV1 struct { // ClinicId Clinic identifier. + // + // Example: 2fe2488217ee43e1b2e83c2f ClinicId *ClinicIdV1 `json:"clinicId,omitempty"` // ExtremeHighGlucoseThreshold Minimum inclusive threshold in mmol/L for categorizing if a glucose value is extremely high. Not defined by the AACE. @@ -1881,10 +3708,13 @@ type TideConfigV1 struct { // LowGlucoseThreshold Maximum exclusive threshold in mmol/L for categorizing if a glucose value is low as established by the AACE. LowGlucoseThreshold float64 `json:"lowGlucoseThreshold"` - Period string `json:"period"` + + // Period Example: 7d + Period string `json:"period"` // SchemaVersion TIDE schema version SchemaVersion int `json:"schemaVersion"` + Sites *SiteIdsV1 `json:"sites,omitempty,omitzero"` Tags *PatientTagIdsV1 `json:"tags"` // VeryHighGlucoseThreshold Minimum exclusive threshold in mmol/L for categorizing if a glucose value is very high as established by the AACE. @@ -1896,19 +3726,34 @@ type TideConfigV1 struct { // TideFiltersV1 Visual representation of filtered categories selected type TideFiltersV1 struct { + // DropInTimeInTargetPercent Example: >0.5 DropInTimeInTargetPercent *string `json:"dropInTimeInTargetPercent,omitempty"` - TimeCGMUsePercent *string `json:"timeCGMUsePercent,omitempty"` - TimeInAnyLowPercent *string `json:"timeInAnyLowPercent,omitempty"` - TimeInExtremeHighPercent *string `json:"timeInExtremeHighPercent,omitempty"` - TimeInHighPercent *string `json:"timeInHighPercent,omitempty"` - TimeInTargetPercent *string `json:"timeInTargetPercent,omitempty"` - TimeInVeryHighPercent *string `json:"timeInVeryHighPercent,omitempty"` - TimeInVeryLowPercent *string `json:"timeInVeryLowPercent,omitempty"` + + // TimeCGMUsePercent Example: >0.5 + TimeCGMUsePercent *string `json:"timeCGMUsePercent,omitempty"` + + // TimeInAnyLowPercent Example: >0.5 + TimeInAnyLowPercent *string `json:"timeInAnyLowPercent,omitempty"` + + // TimeInExtremeHighPercent Example: >0.5 + TimeInExtremeHighPercent *string `json:"timeInExtremeHighPercent,omitempty"` + + // TimeInHighPercent Example: >0.5 + TimeInHighPercent *string `json:"timeInHighPercent,omitempty"` + + // TimeInTargetPercent Example: >0.5 + TimeInTargetPercent *string `json:"timeInTargetPercent,omitempty"` + + // TimeInVeryHighPercent Example: >0.5 + TimeInVeryHighPercent *string `json:"timeInVeryHighPercent,omitempty"` + + // TimeInVeryLowPercent Example: >0.5 + TimeInVeryLowPercent *string `json:"timeInVeryLowPercent,omitempty"` } // TidePatientV1 defines model for tidePatient.v1. type TidePatientV1 struct { - DataSources *[]DataSourceV1 `json:"dataSources"` + DataSources *[]DataSourceV1 `json:"dataSources,omitempty"` Email *string `json:"email,omitempty"` // FullName The full name of the patient @@ -1916,7 +3761,7 @@ type TidePatientV1 struct { // Id String representation of a Tidepool User ID. Old style IDs are 10-digit strings consisting of only hexadeximcal digits. New style IDs are 36-digit [UUID v4](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)) Id *Tidepooluserid `json:"id,omitempty"` - Reviews []PatientReviewV1 `json:"reviews"` + Reviews []PatientReviewV1 `json:"reviews,omitempty"` Tags *PatientTagIdsV1 `json:"tags"` } @@ -1929,44 +3774,70 @@ type TideResponseV1 struct { // TideResultPatientV1 defines model for tideResultPatient.v1. type TideResultPatientV1 struct { // AverageGlucoseMmol Average Glucose of records in this period + // + // Example: 5.5 AverageGlucoseMmol *float64 `json:"averageGlucoseMmol,omitempty"` // GlucoseManagementIndicator A derived value which emulates A1C + // + // Example: 7.5 GlucoseManagementIndicator *float64 `json:"glucoseManagementIndicator,omitempty"` LastData *time.Time `json:"lastData,omitempty"` Patient TidePatientV1 `json:"patient"` // TimeCGMUseMinutes Counter of minutes spent wearing a cgm + // + // Example: 200 TimeCGMUseMinutes *int `json:"timeCGMUseMinutes,omitempty"` // TimeCGMUsePercent Percentage of time spent wearing a cgm + // + // Example: 0.35 TimeCGMUsePercent *float64 `json:"timeCGMUsePercent,omitempty"` // TimeInAnyHighPercent Percentage of time spent in any high glucose range + // + // Example: 0.35 TimeInAnyHighPercent *float64 `json:"timeInAnyHighPercent,omitempty"` // TimeInAnyLowPercent Percentage of time spent in any low glucose range + // + // Example: 0.35 TimeInAnyLowPercent *float64 `json:"timeInAnyLowPercent,omitempty"` // TimeInExtremeHighPercent Percentage of time spent in extreme high glucose range + // + // Example: 0.35 TimeInExtremeHighPercent *float64 `json:"timeInExtremeHighPercent,omitempty"` // TimeInHighPercent Percentage of time spent in high glucose range + // + // Example: 0.35 TimeInHighPercent *float64 `json:"timeInHighPercent,omitempty"` // TimeInLowPercent Percentage of time spent in low glucose range + // + // Example: 0.35 TimeInLowPercent *float64 `json:"timeInLowPercent,omitempty"` // TimeInTargetPercent Percentage of time spent in target glucose range + // + // Example: 0.35 TimeInTargetPercent *float64 `json:"timeInTargetPercent,omitempty"` // TimeInTargetPercentDelta Difference between the timeInTargetPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInTargetPercentDelta *float64 `json:"timeInTargetPercentDelta,omitempty"` // TimeInVeryHighPercent Percentage of time spent in very high glucose range + // + // Example: 0.35 TimeInVeryHighPercent *float64 `json:"timeInVeryHighPercent,omitempty"` // TimeInVeryLowPercent Percentage of time spent in very low glucose range + // + // Example: 0.35 TimeInVeryLowPercent *float64 `json:"timeInVeryLowPercent,omitempty"` } @@ -1979,7 +3850,7 @@ type TidepoolUserIdsV1 = []Tidepooluserid // Tidepooluserid String representation of a Tidepool User ID. Old style IDs are 10-digit strings consisting of only hexadeximcal digits. New style IDs are 36-digit [UUID v4](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)) type Tidepooluserid = string -// TierV1 defines model for tier.v1. +// TierV1 Example: tier0100 type TierV1 string // TriggerMigrationV1 defines model for triggerMigration.v1. @@ -2044,6 +3915,8 @@ type SiteId = ObjectIdV1 type Sort = string // SummaryId Summary Unique Identifier +// +// Example: 2fe2488217ee43e1b2e83c2f type SummaryId = SummaryIdV1 // UserId String representation of a Tidepool User ID. Old style IDs are 10-digit strings consisting of only hexadeximcal digits. New style IDs are 36-digit [UUID v4](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)) @@ -2460,11 +4333,11 @@ type ListPatientsParams struct { // BgmLastDataTo Exclusive BgmLastDataTo *time.Time `form:"bgm.lastDataTo,omitempty" json:"bgm.lastDataTo,omitempty"` - // Tags Comma-separated list of patient tag IDs - Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"` + // Tags Comma-separated list of patient tag IDs. The single value `_` filters for patients without any tags assigned; combining `_` with tag IDs is rejected. + Tags *[]ObjectIdOrZeroV1 `form:"tags,omitempty" json:"tags,omitempty"` - // Sites Comma-separated list of clinic site IDs - Sites *[]string `form:"sites,omitempty" json:"sites,omitempty"` + // Sites Comma-separated list of clinic site IDs. The single value `_` filters for patients without any sites assigned; combining `_` with site IDs is rejected. + Sites *[]ObjectIdOrZeroV1 `form:"sites,omitempty" json:"sites,omitempty"` // OmitNonStandardRanges Whether patients whose glycemic ranges selection is *not* // the ADA standard ranges (e.g. as used by the TIDE report) @@ -2484,10 +4357,13 @@ type TideReportParams struct { LastDataCutoff time.Time `form:"lastDataCutoff" json:"lastDataCutoff"` // Categories Comma-separated list of TIDE report categories to return in queried order. If omitted or empty, the default TIDE categories will be returned - see example. - Categories []TideReportParamsCategories `form:"categories,omitempty" json:"categories,omitempty"` + Categories []TideReportParamsCategories `form:"categories,omitempty" json:"categories,omitempty,omitzero"` // ExcludeNoData If true, then exclude / omit patients with no data in the TIDE report. ExcludeNoData bool `form:"excludeNoData,omitempty" json:"excludeNoData,omitempty"` + + // Sites Comma-separated list of clinic site IDs. If provided, only patients assigned to at least one of the given sites are included in the report. An empty value is ignored, as if the parameter were omitted. + Sites *[]ObjectIdV1 `form:"sites,omitempty" json:"sites,omitempty,omitzero"` } // TideReportParamsCategories defines parameters for TideReport. diff --git a/api/main.go b/api/main.go index f1e979084..9db66484d 100644 --- a/api/main.go +++ b/api/main.go @@ -80,7 +80,7 @@ func SetReady(healthCheck *HealthCheck, db *mongo.Database, lifecycle fx.Lifecyc func NewServer(handler Handler, healthCheck *HealthCheck, authorizer auth.RequestAuthorizer, authenticator auth.Authenticator, logger *zap.Logger) (*echo.Echo, error) { e := echo.New() logger.Info("Starting Main Loop") - swagger, err := GetSwagger() + swagger, err := GetSpec() if err != nil { return nil, err } diff --git a/api/xealth.go b/api/xealth.go index b230f6dd0..8da93aad2 100644 --- a/api/xealth.go +++ b/api/xealth.go @@ -45,7 +45,7 @@ func (h *Handler) XealthPreorder(ec echo.Context) error { } switch eventMeta.EventContext { - case string(xealth_client.Initial): + case string(xealth_client.PreorderFormRequest0EventContextInitial): initial, err := request.AsPreorderFormRequest0() if err != nil { return err @@ -56,7 +56,7 @@ func (h *Handler) XealthPreorder(ec echo.Context) error { return err } return ec.JSON(http.StatusOK, response) - case string(xealth_client.Subsequent): + case string(xealth_client.PreorderFormRequest1EventContextSubsequent): subsequent, err := request.AsPreorderFormRequest1() if err != nil { return err diff --git a/client/client.go b/client/client.go index 4d82a4074..f85ca04e7 100644 --- a/client/client.go +++ b/client/client.go @@ -1,6 +1,6 @@ // Package client provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.8.0 DO NOT EDIT. package client import ( @@ -17,7 +17,7 @@ import ( "github.com/oapi-codegen/runtime" ) -// RequestEditorFn is the function signature for the RequestEditor callback function +// RequestEditorFn is the function signature for the RequestEditor callback function type RequestEditorFn func(ctx context.Context, req *http.Request) error // Doer performs HTTP requests. @@ -90,304 +90,931 @@ func WithRequestEditorFn(fn RequestEditorFn) ClientOption { // The interface specification for the client above. type ClientInterface interface { - // ListAllClinicians request + + // ListAllClinicians List All Clinicians + // + // Returns list of all clinician/clinic relationships in the system. Internal endpoint used for reporting purposes. + // + // Corresponds with GET /v1/clinicians (the `ListAllClinicians` operationId). ListAllClinicians(ctx context.Context, params *ListAllCliniciansParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // ListClinicsForClinician request + // ListClinicsForClinician List Clinics for Clinician + // + // Returns a list of all clinics a clinician is a member of. + // + // Corresponds with GET /v1/clinicians/{userId}/clinics (the `ListClinicsForClinician` operationId). ListClinicsForClinician(ctx context.Context, userId UserId, params *ListClinicsForClinicianParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // EnableNewClinicExperience request + // EnableNewClinicExperience Enable Clinics + // + // Internal only endpoint to enable new clinic experience for a legacy clinician account. Patients won't be migrated until the clinic details are populated and migration is explicitly kicked-off by blip. + // + // Corresponds with POST /v1/clinicians/{userId}/migrate (the `EnableNewClinicExperience` operationId). EnableNewClinicExperience(ctx context.Context, userId string, reqEditors ...RequestEditorFn) (*http.Response, error) - // ListClinics request + // ListClinics List Clinics + // + // Retrieve the list of clinics. + // + // Corresponds with GET /v1/clinics (the `ListClinics` operationId). ListClinics(ctx context.Context, params *ListClinicsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // CreateClinicWithBody request with any body + // CreateClinicWithBody Create Clinic + // + // Create a new clinic. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics (the `CreateClinic` operationId). CreateClinicWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // CreateClinic Create Clinic + // + // Create a new clinic. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics (the `CreateClinic` operationId). CreateClinic(ctx context.Context, body CreateClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetClinicByShareCode request + // GetClinicByShareCode Get Clinic by Share Code + // + // Retrieve a clinic object with a share code. + // + // Corresponds with GET /v1/clinics/share_code/{shareCode} (the `GetClinicByShareCode` operationId). GetClinicByShareCode(ctx context.Context, shareCode string, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeleteClinic request + // DeleteClinic Delete Clinic + // + // Deletes a clinic if there are no connected patient accounts (besides demo accounts). + // + // Corresponds with DELETE /v1/clinics/{clinicId} (the `DeleteClinic` operationId). DeleteClinic(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetClinic request + // GetClinic Get Clinic + // + // Retrieve a clinic by id. + // + // Corresponds with GET /v1/clinics/{clinicId} (the `GetClinic` operationId). GetClinic(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateClinicWithBody request with any body + // UpdateClinicWithBody Update Clinic + // + // Update an existing clinic. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId} (the `UpdateClinic` operationId). UpdateClinicWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateClinic Update Clinic + // + // Update an existing clinic. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId} (the `UpdateClinic` operationId). UpdateClinic(ctx context.Context, clinicId ClinicId, body UpdateClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // ListClinicians request + // ListClinicians List Clinicians + // + // Retrieve the list of clinic members. + // + // Corresponds with GET /v1/clinics/{clinicId}/clinicians (the `ListClinicians` operationId). ListClinicians(ctx context.Context, clinicId ClinicId, params *ListCliniciansParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // CreateClinicianWithBody request with any body + // CreateClinicianWithBody Create Clinician + // + // Internal endpoint to create a new clinician. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/clinicians (the `CreateClinician` operationId). CreateClinicianWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // CreateClinician Create Clinician + // + // Internal endpoint to create a new clinician. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/clinicians (the `CreateClinician` operationId). CreateClinician(ctx context.Context, clinicId ClinicId, body CreateClinicianJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeleteClinician request + // DeleteClinician Delete Clinician + // + // Removes a clinician from a clinic. + // + // Corresponds with DELETE /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `DeleteClinician` operationId). DeleteClinician(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetClinician request + // GetClinician Get Clinician + // + // Retrieve a member of clinic given their user id. + // + // Corresponds with GET /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `GetClinician` operationId). GetClinician(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateClinicianWithBody request with any body + // UpdateClinicianWithBody Update Clinician + // + // Update existing clinician. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `UpdateClinician` operationId). UpdateClinicianWithBody(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateClinician Update Clinician + // + // Update existing clinician. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `UpdateClinician` operationId). UpdateClinician(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, body UpdateClinicianJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // SyncEHRData request + // SyncEHRData Sync EHR Data + // + // An internal endpoint which will push the latest patient summary statistics and PDF reports for all patients who have an active subscription. The actual data is pushed asynchronously. A successful response means that an asynchronous task has been scheduled for each patient of the clinic with an active subscription. + // + // Will return 404 Not Found if the clinic does not have an active EHR connection. + // + // Corresponds with POST /v1/clinics/{clinicId}/ehr/sync (the `SyncEHRData` operationId). SyncEHRData(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeleteInvitedClinician request + // DeleteInvitedClinician Delete Invited Clinician + // + // Internal endpoint to delete an invited clinician object. + // + // Corresponds with DELETE /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `DeleteInvitedClinician` operationId). DeleteInvitedClinician(ctx context.Context, clinicId ClinicId, inviteId InviteId, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetInvitedClinician request + // GetInvitedClinician Get Invited Clinician + // + // Internal endpoint to retrieve invited clinician. + // + // Corresponds with GET /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `GetInvitedClinician` operationId). GetInvitedClinician(ctx context.Context, clinicId ClinicId, inviteId InviteId, reqEditors ...RequestEditorFn) (*http.Response, error) - // AssociateClinicianToUserWithBody request with any body + // AssociateClinicianToUserWithBody Associate Clinician to User + // + // Internal endpoint to associate a clinician to a user. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PATCH /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `AssociateClinicianToUser` operationId). AssociateClinicianToUserWithBody(ctx context.Context, clinicId ClinicId, inviteId InviteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // AssociateClinicianToUser Associate Clinician to User + // + // Internal endpoint to associate a clinician to a user. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PATCH /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `AssociateClinicianToUser` operationId). AssociateClinicianToUser(ctx context.Context, clinicId ClinicId, inviteId InviteId, body AssociateClinicianToUserJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // ListMembershipRestrictions request + // ListMembershipRestrictions List Membership Restrictions + // + // Returns a list of the membership restrictions that will be evaluated when a user joins a clinic. + // + // Only clinic admins can access this endpoint. + // + // Corresponds with GET /v1/clinics/{clinicId}/membership_restrictions (the `ListMembershipRestrictions` operationId). ListMembershipRestrictions(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateMembershipRestrictionsWithBody request with any body + // UpdateMembershipRestrictionsWithBody Update Membership Restrictions + // + // An internal endpoint to update clinic membership restrictions. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/membership_restrictions (the `UpdateMembershipRestrictions` operationId). UpdateMembershipRestrictionsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateMembershipRestrictions Update Membership Restrictions + // + // An internal endpoint to update clinic membership restrictions. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/membership_restrictions (the `UpdateMembershipRestrictions` operationId). UpdateMembershipRestrictions(ctx context.Context, clinicId ClinicId, body UpdateMembershipRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // MergeClinicWithBody request with any body + // MergeClinicWithBody Merge Clinic + // + // Merges tags, patients, clinicians, invites and share codes of the source clinic + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/merge (the `MergeClinic` operationId). MergeClinicWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // MergeClinic Merge Clinic + // + // Merges tags, patients, clinicians, invites and share codes of the source clinic + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/merge (the `MergeClinic` operationId). MergeClinic(ctx context.Context, clinicId ClinicId, body MergeClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // TriggerInitialMigrationWithBody request with any body + // TriggerInitialMigrationWithBody Trigger initial migration + // + // Trigger the initial migration. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/migrate (the `TriggerInitialMigration` operationId). TriggerInitialMigrationWithBody(ctx context.Context, clinicId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // TriggerInitialMigration Trigger initial migration + // + // Trigger the initial migration. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/migrate (the `TriggerInitialMigration` operationId). TriggerInitialMigration(ctx context.Context, clinicId string, body TriggerInitialMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // ListMigrations request + // ListMigrations Retrieve Migration Status + // + // ListMigrations. + // + // Corresponds with GET /v1/clinics/{clinicId}/migrations (the `ListMigrations` operationId). ListMigrations(ctx context.Context, clinicId string, reqEditors ...RequestEditorFn) (*http.Response, error) - // MigrateLegacyClinicianPatientsWithBody request with any body + // MigrateLegacyClinicianPatientsWithBody Migrate Legacy Clinician Patients + // + // Migrate the patients of a legacy clinic account. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/migrations (the `MigrateLegacyClinicianPatients` operationId). MigrateLegacyClinicianPatientsWithBody(ctx context.Context, clinicId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // MigrateLegacyClinicianPatients Migrate Legacy Clinician Patients + // + // Migrate the patients of a legacy clinic account. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/migrations (the `MigrateLegacyClinicianPatients` operationId). MigrateLegacyClinicianPatients(ctx context.Context, clinicId string, body MigrateLegacyClinicianPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetMigration request + // GetMigration Get Migration + // + // Get a migration by user id. + // + // Corresponds with GET /v1/clinics/{clinicId}/migrations/{userId} (the `GetMigration` operationId). GetMigration(ctx context.Context, clinicId ClinicIdV1, userId UserId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateMigrationWithBody request with any body + // UpdateMigrationWithBody Update Migration + // + // Internal endpoint to update the status of a migration. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PATCH /v1/clinics/{clinicId}/migrations/{userId} (the `UpdateMigration` operationId). UpdateMigrationWithBody(ctx context.Context, clinicId ClinicIdV1, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateMigration Update Migration + // + // Internal endpoint to update the status of a migration. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PATCH /v1/clinics/{clinicId}/migrations/{userId} (the `UpdateMigration` operationId). UpdateMigration(ctx context.Context, clinicId ClinicIdV1, userId UserId, body UpdateMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetPatientCount request + // GetPatientCount Get Patient Count + // + // Corresponds with GET /v1/clinics/{clinicId}/patient_count (the `GetPatientCount` operationId). GetPatientCount(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) - // RefreshPatientCount request + // RefreshPatientCount Refresh Patient Count + // + // An internal endpoint to refresh the patient count for the specified clinic. + // + // Corresponds with POST /v1/clinics/{clinicId}/patient_count/refresh (the `RefreshPatientCount` operationId). RefreshPatientCount(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) - // CreatePatientTagWithBody request with any body + // CreatePatientTagWithBody Create Patient Tag + // + // Create a new patient tag. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patient_tags (the `CreatePatientTag` operationId). CreatePatientTagWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // CreatePatientTag Create Patient Tag + // + // Create a new patient tag. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patient_tags (the `CreatePatientTag` operationId). CreatePatientTag(ctx context.Context, clinicId ClinicId, body CreatePatientTagJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeletePatientTag request + // DeletePatientTag Delete Patient Tag + // + // Removes a patient tag from a clinic and tagged patients. + // + // Corresponds with DELETE /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `DeletePatientTag` operationId). DeletePatientTag(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdatePatientTagWithBody request with any body + // UpdatePatientTagWithBody Update Patient Tag + // + // Update existing patient tag. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `UpdatePatientTag` operationId). UpdatePatientTagWithBody(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdatePatientTag Update Patient Tag + // + // Update existing patient tag. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `UpdatePatientTag` operationId). UpdatePatientTag(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body UpdatePatientTagJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // ConvertPatientTagToSite request + // ConvertPatientTagToSite Convert Patient Tag to Site + // + // Convert a patient tag to a site. + // + // Corresponds with POST /v1/clinics/{clinicId}/patient_tags/{patientTagId}/site (the `ConvertPatientTagToSite` operationId). ConvertPatientTagToSite(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, reqEditors ...RequestEditorFn) (*http.Response, error) - // ListPatients request + // ListPatients List Patients + // + // Retrieve a list of patients of a clinic. + // + // Corresponds with GET /v1/clinics/{clinicId}/patients (the `ListPatients` operationId). ListPatients(ctx context.Context, clinicId ClinicId, params *ListPatientsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // CreatePatientAccountWithBody request with any body + // CreatePatientAccountWithBody Create Patient Account + // + // Create a custodial account for a patient. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients (the `CreatePatientAccount` operationId). CreatePatientAccountWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // CreatePatientAccount Create Patient Account + // + // Create a custodial account for a patient. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients (the `CreatePatientAccount` operationId). CreatePatientAccount(ctx context.Context, clinicId ClinicId, body CreatePatientAccountJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // AssignPatientTagToClinicPatientsWithBody request with any body + // AssignPatientTagToClinicPatientsWithBody Assign Patient Tag To Clinic Patients + // + // Assign a patient tag to a subset of clinic patients. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/assign_tag/{patientTagId} (the `AssignPatientTagToClinicPatients` operationId). AssignPatientTagToClinicPatientsWithBody(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // AssignPatientTagToClinicPatients Assign Patient Tag To Clinic Patients + // + // Assign a patient tag to a subset of clinic patients. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/assign_tag/{patientTagId} (the `AssignPatientTagToClinicPatients` operationId). AssignPatientTagToClinicPatients(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body AssignPatientTagToClinicPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeletePatientTagFromClinicPatientsWithBody request with any body + // DeletePatientTagFromClinicPatientsWithBody Delete Patient Tag From Clinic Patients + // + // Delete a patient tag from all or a subset of clinic patients. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/delete_tag/{patientTagId} (the `DeletePatientTagFromClinicPatients` operationId). DeletePatientTagFromClinicPatientsWithBody(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // DeletePatientTagFromClinicPatients Delete Patient Tag From Clinic Patients + // + // Delete a patient tag from all or a subset of clinic patients. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/delete_tag/{patientTagId} (the `DeletePatientTagFromClinicPatients` operationId). DeletePatientTagFromClinicPatients(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body DeletePatientTagFromClinicPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeletePatient request + // DeletePatient Delete Patient + // + // Deletes patient from a clinic. Requires the request to be made by a clinic admin or the currently authenticated user id to match the patient id. + // + // Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId} (the `DeletePatient` operationId). DeletePatient(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetPatient request + // GetPatient Get Patient + // + // Retrieve a patient of clinic given their user id. + // + // Corresponds with GET /v1/clinics/{clinicId}/patients/{patientId} (the `GetPatient` operationId). GetPatient(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) - // CreatePatientFromUserWithBody request with any body + // CreatePatientFromUserWithBody Create Patient from Existing User + // + // Internal endpoint for creating a patient from an existing user. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId} (the `CreatePatientFromUser` operationId). CreatePatientFromUserWithBody(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // CreatePatientFromUser Create Patient from Existing User + // + // Internal endpoint for creating a patient from an existing user. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId} (the `CreatePatientFromUser` operationId). CreatePatientFromUser(ctx context.Context, clinicId ClinicId, patientId PatientId, body CreatePatientFromUserJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdatePatientWithBody request with any body + // UpdatePatientWithBody Update Patient + // + // Update existing patient of a clinic. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId} (the `UpdatePatient` operationId). UpdatePatientWithBody(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdatePatient Update Patient + // + // Update existing patient of a clinic. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId} (the `UpdatePatient` operationId). UpdatePatient(ctx context.Context, clinicId ClinicId, patientId PatientId, body UpdatePatientJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // ConnectProvider request + // ConnectProvider Connect Provider + // + // Send a new request to the patient to connect a data provider. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId}/connect/{providerId} (the `ConnectProvider` operationId). ConnectProvider(ctx context.Context, clinicId ClinicId, patientId PatientId, providerId ProviderId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdatePatientPermissionsWithBody request with any body + // UpdatePatientPermissionsWithBody Update Patient Permissions + // + // Update permissions that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. + // + // Only patients can change the permissions. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/permissions (the `UpdatePatientPermissions` operationId). UpdatePatientPermissionsWithBody(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdatePatientPermissions Update Patient Permissions + // + // Update permissions that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. + // + // Only patients can change the permissions. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/permissions (the `UpdatePatientPermissions` operationId). UpdatePatientPermissions(ctx context.Context, clinicId ClinicId, patientId PatientId, body UpdatePatientPermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeletePatientPermission request + // DeletePatientPermission Delete Patient Permission + // + // Remove a single permission that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. + // + // Only patients can change the permissions. + // + // Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId}/permissions/{permission} (the `DeletePatientPermission` operationId). DeletePatientPermission(ctx context.Context, clinicId ClinicId, patientId PatientId, permission string, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeletePatientReviews request + // DeletePatientReviews Delete Patient Reviews + // + // Revert the patient's last reviewed date to the previous set. + // + // Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId}/reviews (the `DeletePatientReviews` operationId). DeletePatientReviews(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdatePatientReviews request + // UpdatePatientReviews Update Patient Reviews + // + // Update the patient's last reviewed date and clinician ID. + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/reviews (the `UpdatePatientReviews` operationId). UpdatePatientReviews(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) - // SendUploadReminder request + // SendUploadReminder Send Upload Reminder + // + // Sends upload reminder to the patient. + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId}/upload_reminder (the `SendUploadReminder` operationId). SendUploadReminder(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) - // GenerateMergeReportWithBody request with any body + // GenerateMergeReportWithBody Generate Clinic Merge Report + // + // Generates a spreadsheet report for merging two clinics. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/reports/merge (the `GenerateMergeReport` operationId). GenerateMergeReportWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // GenerateMergeReport Generate Clinic Merge Report + // + // Generates a spreadsheet report for merging two clinics. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/reports/merge (the `GenerateMergeReport` operationId). GenerateMergeReport(ctx context.Context, clinicId ClinicId, body GenerateMergeReportJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // AddServiceAccountWithBody request with any body + // AddServiceAccountWithBody Add Service Account + // + // Adds authentication client's service account as a new clinician member. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/service_accounts (the `AddServiceAccount` operationId). AddServiceAccountWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // AddServiceAccount Add Service Account + // + // Adds authentication client's service account as a new clinician member. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/service_accounts (the `AddServiceAccount` operationId). AddServiceAccount(ctx context.Context, clinicId ClinicId, body AddServiceAccountJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetEHRSettings request + // GetEHRSettings Get EHR Settings + // + // Returns EHR related settings. + // + // Corresponds with GET /v1/clinics/{clinicId}/settings/ehr (the `GetEHRSettings` operationId). GetEHRSettings(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateEHRSettingsWithBody request with any body + // UpdateEHRSettingsWithBody Update EHR Settings + // + // Update EHR related settings for a clinic. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/ehr (the `UpdateEHRSettings` operationId). UpdateEHRSettingsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateEHRSettings Update EHR Settings + // + // Update EHR related settings for a clinic. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/ehr (the `UpdateEHRSettings` operationId). UpdateEHRSettings(ctx context.Context, clinicId ClinicId, body UpdateEHRSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetMRNSettings request + // GetMRNSettings Get MRN Settings + // + // Retrieve MRN settings. + // + // Corresponds with GET /v1/clinics/{clinicId}/settings/mrn (the `GetMRNSettings` operationId). GetMRNSettings(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateMRNSettingsWithBody request with any body + // UpdateMRNSettingsWithBody Update MRN Settings + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/mrn (the `UpdateMRNSettings` operationId). UpdateMRNSettingsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateMRNSettings Update MRN Settings + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/mrn (the `UpdateMRNSettings` operationId). UpdateMRNSettings(ctx context.Context, clinicId ClinicId, body UpdateMRNSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetPatientCountSettings request + // GetPatientCountSettings Get Patient Count Settings + // + // Get Patient Count settings. + // + // Corresponds with GET /v1/clinics/{clinicId}/settings/patient_count (the `GetPatientCountSettings` operationId). GetPatientCountSettings(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdatePatientCountSettingsWithBody request with any body + // UpdatePatientCountSettingsWithBody Update Patient Count Settings + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/patient_count (the `UpdatePatientCountSettings` operationId). UpdatePatientCountSettingsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdatePatientCountSettings Update Patient Count Settings + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/patient_count (the `UpdatePatientCountSettings` operationId). UpdatePatientCountSettings(ctx context.Context, clinicId ClinicId, body UpdatePatientCountSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // CreateSiteWithBody request with any body + // CreateSiteWithBody Create a Site + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/sites (the `CreateSite` operationId). CreateSiteWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // CreateSite Create a Site + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/sites (the `CreateSite` operationId). CreateSite(ctx context.Context, clinicId ClinicId, body CreateSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeleteSite request + // DeleteSite Delete a Site + // + // Corresponds with DELETE /v1/clinics/{clinicId}/sites/{siteId} (the `DeleteSite` operationId). DeleteSite(ctx context.Context, clinicId ClinicId, siteId SiteId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateSiteWithBody request with any body + // UpdateSiteWithBody Update a Site + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/sites/{siteId} (the `UpdateSite` operationId). UpdateSiteWithBody(ctx context.Context, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateSite Update a Site + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/clinics/{clinicId}/sites/{siteId} (the `UpdateSite` operationId). UpdateSite(ctx context.Context, clinicId ClinicId, siteId SiteId, body UpdateSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // MergeSiteWithBody request with any body + // MergeSiteWithBody Merge two sites + // + // Merge a site, identified in the request body, into the site identified in the URL. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/sites/{siteId}/merge (the `MergeSite` operationId). MergeSiteWithBody(ctx context.Context, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // MergeSite Merge two sites + // + // Merge a site, identified in the request body, into the site identified in the URL. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/sites/{siteId}/merge (the `MergeSite` operationId). MergeSite(ctx context.Context, clinicId ClinicId, siteId SiteId, body MergeSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateSuppressedNotificationsWithBody request with any body + // UpdateSuppressedNotificationsWithBody Update Suppressed Notifications + // + // Endpoint to update clinic suppressed notifications. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/suppressed_notifications (the `UpdateSuppressedNotifications` operationId). UpdateSuppressedNotificationsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateSuppressedNotifications Update Suppressed Notifications + // + // Endpoint to update clinic suppressed notifications. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/suppressed_notifications (the `UpdateSuppressedNotifications` operationId). UpdateSuppressedNotifications(ctx context.Context, clinicId ClinicId, body UpdateSuppressedNotificationsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // TideReport request + // TideReport View TIDE Report + // + // Retrieve a report of patients with tags by TIDE categories. + // + // Corresponds with GET /v1/clinics/{clinicId}/tide_report (the `TideReport` operationId). TideReport(ctx context.Context, clinicId ClinicId, params *TideReportParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateTierWithBody request with any body + // UpdateTierWithBody Update Tier + // + // Internal endpoint to update clinic service tier. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/tier (the `UpdateTier` operationId). UpdateTierWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateTier Update Tier + // + // Internal endpoint to update clinic service tier. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/clinics/{clinicId}/tier (the `UpdateTier` operationId). UpdateTier(ctx context.Context, clinicId ClinicId, body UpdateTierJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // FindPatients request + // FindPatients Find Patients + // + // Returns all patients to which the clinician has access to. + // + // Corresponds with GET /v1/patients (the `FindPatients` operationId). FindPatients(ctx context.Context, params *FindPatientsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // SyncEHRDataForPatient request + // SyncEHRDataForPatient Sync EHR Data for Patient + // + // An internal endpoint which will push the latest patient summary statistics and PDF reports to all EHR enabled clinics for which the patient has an active subscription. The actual data is pushed asynchronously. A successful response means that an asynchronous task has been scheduled. + // + // A 404 response will be returned if no active subscriptions can be found for given patient. + // + // Corresponds with POST /v1/patients/{patientId}/ehr/sync (the `SyncEHRDataForPatient` operationId). SyncEHRDataForPatient(ctx context.Context, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdatePatientSummaryWithBody request with any body + // UpdatePatientSummaryWithBody UpdatePatientSummary + // + // Internal endpoint which updates the summary in all patient records which match the user id. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/patients/{patientId}/summary (the `UpdatePatientSummary` operationId). UpdatePatientSummaryWithBody(ctx context.Context, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdatePatientSummary UpdatePatientSummary + // + // Internal endpoint which updates the summary in all patient records which match the user id. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/patients/{patientId}/summary (the `UpdatePatientSummary` operationId). UpdatePatientSummary(ctx context.Context, patientId PatientId, body UpdatePatientSummaryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // ListClinicsForPatient request + // ListClinicsForPatient List Clinics for Patient + // + // Get all clinics a user is a patient of. + // + // Corresponds with GET /v1/patients/{userId}/clinics (the `ListClinicsForPatient` operationId). ListClinicsForPatient(ctx context.Context, userId UserId, params *ListClinicsForPatientParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdatePatientDataSourcesWithBody request with any body + // UpdatePatientDataSourcesWithBody Create or update a data source for a patient + // + // Internal endpoint to upsert patient data source provider details in all their associated clinic records. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /v1/patients/{userId}/data_sources (the `UpdatePatientDataSources` operationId). UpdatePatientDataSourcesWithBody(ctx context.Context, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdatePatientDataSources Create or update a data source for a patient + // + // Internal endpoint to upsert patient data source provider details in all their associated clinic records. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /v1/patients/{userId}/data_sources (the `UpdatePatientDataSources` operationId). UpdatePatientDataSources(ctx context.Context, userId UserId, body UpdatePatientDataSourcesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // ProcessEHRMessageWithBody request with any body + // ProcessEHRMessageWithBody Redox EHR Endpoint + // + // An endpoint for incoming Redox messages. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/redox (the `ProcessEHRMessage` operationId). ProcessEHRMessageWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // ProcessEHRMessage Redox EHR Endpoint + // + // An endpoint for incoming Redox messages. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/redox (the `ProcessEHRMessage` operationId). ProcessEHRMessage(ctx context.Context, body ProcessEHRMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // MatchClinicAndPatientWithBody request with any body + // MatchClinicAndPatientWithBody Match Clinic and Patient + // + // An internal endpoint used for matching an EHR request (e.g. NewOrder) to a clinic and patient. + // + // If a clinic with the provided details cannot be found, the service will return `404 Not Found` and will not attempt to match patients to the provided details. + // + // Due to lack of uniqueness constraints on the MRN and DOB fields it's possible that multiple patient records match the provided attributes. All results will be returned in the response and it's the calling services responsiblity to determine how to handle duplicates records. + // + // If a unique match is found, the patient matching criteria will be added to the patient record for future use (e.g. pushing data on a schedule). + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/redox/match (the `MatchClinicAndPatient` operationId). MatchClinicAndPatientWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // MatchClinicAndPatient Match Clinic and Patient + // + // An internal endpoint used for matching an EHR request (e.g. NewOrder) to a clinic and patient. + // + // If a clinic with the provided details cannot be found, the service will return `404 Not Found` and will not attempt to match patients to the provided details. + // + // Due to lack of uniqueness constraints on the MRN and DOB fields it's possible that multiple patient records match the provided attributes. All results will be returned in the response and it's the calling services responsiblity to determine how to handle duplicates records. + // + // If a unique match is found, the patient matching criteria will be added to the patient record for future use (e.g. pushing data on a schedule). + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/redox/match (the `MatchClinicAndPatient` operationId). MatchClinicAndPatient(ctx context.Context, body MatchClinicAndPatientJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // VerifyEndpoint request + // VerifyEndpoint Redox Verify Endpoint + // + // Internal endpoint for Redox challenge verification. + // + // Corresponds with POST /v1/redox/verify (the `VerifyEndpoint` operationId). VerifyEndpoint(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeletePatientSummary request + // DeletePatientSummary DeletePatientSummary + // + // Internal endpoint which deletes the summary in all patient records which match the summary id. + // + // Corresponds with DELETE /v1/summaries/{summaryId}/clinics (the `DeletePatientSummary` operationId). DeletePatientSummary(ctx context.Context, summaryId SummaryId, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeleteUserFromClinics request + // DeleteUserFromClinics Remove User from Clinics + // + // Internal endpoint that deletes any user associations with clinics they are a patient or a member of. + // + // If the user is a patient of a clinic the service will delete the patient-clinic relationship. + // + // If the user is one of many admins of any clinic the service will delete the clinician-clinic relationship. + // + // If the user is the only admin of a clinic the service will: + // - delete the clinician record, leaving the clinic without an admin + // - delete all non-custodial patient relationships of the clinic + // - keep all custodian relationships, allowing users to claim custodial accounts with an email + // - keep all custodian accounts and data even if they can’t be claimed when there isn’t an email address associated with the account + // + // Corresponds with DELETE /v1/users/{userId}/clinics (the `DeleteUserFromClinics` operationId). DeleteUserFromClinics(ctx context.Context, userId UserId, reqEditors ...RequestEditorFn) (*http.Response, error) - // UpdateClinicUserDetailsWithBody request with any body + // UpdateClinicUserDetailsWithBody Update User Details + // + // Internal endpoint to update clinician details. In the future it may be extended to support patient updates. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /v1/users/{userId}/clinics (the `UpdateClinicUserDetails` operationId). UpdateClinicUserDetailsWithBody(ctx context.Context, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateClinicUserDetails Update User Details + // + // Internal endpoint to update clinician details. In the future it may be extended to support patient updates. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /v1/users/{userId}/clinics (the `UpdateClinicUserDetails` operationId). UpdateClinicUserDetails(ctx context.Context, userId UserId, body UpdateClinicUserDetailsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // XealthNotification request + // XealthNotification Notification Webhook + // + // Notification webhook. + // + // Corresponds with POST /v1/xealth/notification (the `XealthNotification` operationId). XealthNotification(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - // XealthPreorder request + // XealthPreorder Preorder Form Webhook + // + // The Preorder endpoint can be invoked when a care provider attempts to place an order manually within the EHR. Subscribers can use Preorder Forms to present additional information to, or receive input from, a care provider before an order can be placed. Preorder Forms can also be implemented so as to prevent an order from being placed entirely. + // + // https://sandbox.xealth.io/app/api-docs#tag/Digital-Care/paths/preorder/post + // + // Corresponds with POST /v1/xealth/preorder (the `XealthPreorder` operationId). XealthPreorder(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - // XealthGetProgramUrl request + // XealthGetProgramUrl Get Program Url + // + // Get program URL. + // + // Corresponds with PUT /v1/xealth/program (the `XealthGetProgramUrl` operationId). XealthGetProgramUrl(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - // XealthGetPrograms request + // XealthGetPrograms Get Programs + // + // Get programs. + // + // Corresponds with PUT /v1/xealth/programs (the `XealthGetPrograms` operationId). XealthGetPrograms(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - // ViewPDFReport request + // ViewPDFReport View PDF Report + // + // View PDF report for a given patient of a clinic. + // + // Corresponds with GET /v1/xealth/report/web/viewer.html (the `ViewPDFReport` operationId). ViewPDFReport(ctx context.Context, params *ViewPDFReportParams, reqEditors ...RequestEditorFn) (*http.Response, error) } +// ListAllClinicians List All Clinicians +// +// Returns list of all clinician/clinic relationships in the system. Internal endpoint used for reporting purposes. +// +// Corresponds with GET /v1/clinicians (the `ListAllClinicians` operationId). func (c *Client) ListAllClinicians(ctx context.Context, params *ListAllCliniciansParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListAllCliniciansRequest(c.Server, params) if err != nil { @@ -400,6 +1027,11 @@ func (c *Client) ListAllClinicians(ctx context.Context, params *ListAllClinician return c.Client.Do(req) } +// ListClinicsForClinician List Clinics for Clinician +// +// Returns a list of all clinics a clinician is a member of. +// +// Corresponds with GET /v1/clinicians/{userId}/clinics (the `ListClinicsForClinician` operationId). func (c *Client) ListClinicsForClinician(ctx context.Context, userId UserId, params *ListClinicsForClinicianParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListClinicsForClinicianRequest(c.Server, userId, params) if err != nil { @@ -412,6 +1044,11 @@ func (c *Client) ListClinicsForClinician(ctx context.Context, userId UserId, par return c.Client.Do(req) } +// EnableNewClinicExperience Enable Clinics +// +// Internal only endpoint to enable new clinic experience for a legacy clinician account. Patients won't be migrated until the clinic details are populated and migration is explicitly kicked-off by blip. +// +// Corresponds with POST /v1/clinicians/{userId}/migrate (the `EnableNewClinicExperience` operationId). func (c *Client) EnableNewClinicExperience(ctx context.Context, userId string, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewEnableNewClinicExperienceRequest(c.Server, userId) if err != nil { @@ -424,6 +1061,11 @@ func (c *Client) EnableNewClinicExperience(ctx context.Context, userId string, r return c.Client.Do(req) } +// ListClinics List Clinics +// +// Retrieve the list of clinics. +// +// Corresponds with GET /v1/clinics (the `ListClinics` operationId). func (c *Client) ListClinics(ctx context.Context, params *ListClinicsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListClinicsRequest(c.Server, params) if err != nil { @@ -436,6 +1078,13 @@ func (c *Client) ListClinics(ctx context.Context, params *ListClinicsParams, req return c.Client.Do(req) } +// CreateClinicWithBody Create Clinic +// +// Create a new clinic. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics (the `CreateClinic` operationId). func (c *Client) CreateClinicWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreateClinicRequestWithBody(c.Server, contentType, body) if err != nil { @@ -448,6 +1097,13 @@ func (c *Client) CreateClinicWithBody(ctx context.Context, contentType string, b return c.Client.Do(req) } +// CreateClinic Create Clinic +// +// Create a new clinic. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics (the `CreateClinic` operationId). func (c *Client) CreateClinic(ctx context.Context, body CreateClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreateClinicRequest(c.Server, body) if err != nil { @@ -460,6 +1116,11 @@ func (c *Client) CreateClinic(ctx context.Context, body CreateClinicJSONRequestB return c.Client.Do(req) } +// GetClinicByShareCode Get Clinic by Share Code +// +// Retrieve a clinic object with a share code. +// +// Corresponds with GET /v1/clinics/share_code/{shareCode} (the `GetClinicByShareCode` operationId). func (c *Client) GetClinicByShareCode(ctx context.Context, shareCode string, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetClinicByShareCodeRequest(c.Server, shareCode) if err != nil { @@ -472,6 +1133,11 @@ func (c *Client) GetClinicByShareCode(ctx context.Context, shareCode string, req return c.Client.Do(req) } +// DeleteClinic Delete Clinic +// +// Deletes a clinic if there are no connected patient accounts (besides demo accounts). +// +// Corresponds with DELETE /v1/clinics/{clinicId} (the `DeleteClinic` operationId). func (c *Client) DeleteClinic(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeleteClinicRequest(c.Server, clinicId) if err != nil { @@ -484,6 +1150,11 @@ func (c *Client) DeleteClinic(ctx context.Context, clinicId ClinicId, reqEditors return c.Client.Do(req) } +// GetClinic Get Clinic +// +// Retrieve a clinic by id. +// +// Corresponds with GET /v1/clinics/{clinicId} (the `GetClinic` operationId). func (c *Client) GetClinic(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetClinicRequest(c.Server, clinicId) if err != nil { @@ -496,6 +1167,13 @@ func (c *Client) GetClinic(ctx context.Context, clinicId ClinicId, reqEditors .. return c.Client.Do(req) } +// UpdateClinicWithBody Update Clinic +// +// Update an existing clinic. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId} (the `UpdateClinic` operationId). func (c *Client) UpdateClinicWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateClinicRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -508,6 +1186,13 @@ func (c *Client) UpdateClinicWithBody(ctx context.Context, clinicId ClinicId, co return c.Client.Do(req) } +// UpdateClinic Update Clinic +// +// Update an existing clinic. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId} (the `UpdateClinic` operationId). func (c *Client) UpdateClinic(ctx context.Context, clinicId ClinicId, body UpdateClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateClinicRequest(c.Server, clinicId, body) if err != nil { @@ -520,6 +1205,11 @@ func (c *Client) UpdateClinic(ctx context.Context, clinicId ClinicId, body Updat return c.Client.Do(req) } +// ListClinicians List Clinicians +// +// Retrieve the list of clinic members. +// +// Corresponds with GET /v1/clinics/{clinicId}/clinicians (the `ListClinicians` operationId). func (c *Client) ListClinicians(ctx context.Context, clinicId ClinicId, params *ListCliniciansParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListCliniciansRequest(c.Server, clinicId, params) if err != nil { @@ -532,6 +1222,13 @@ func (c *Client) ListClinicians(ctx context.Context, clinicId ClinicId, params * return c.Client.Do(req) } +// CreateClinicianWithBody Create Clinician +// +// Internal endpoint to create a new clinician. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/clinicians (the `CreateClinician` operationId). func (c *Client) CreateClinicianWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreateClinicianRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -544,6 +1241,13 @@ func (c *Client) CreateClinicianWithBody(ctx context.Context, clinicId ClinicId, return c.Client.Do(req) } +// CreateClinician Create Clinician +// +// Internal endpoint to create a new clinician. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/clinicians (the `CreateClinician` operationId). func (c *Client) CreateClinician(ctx context.Context, clinicId ClinicId, body CreateClinicianJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreateClinicianRequest(c.Server, clinicId, body) if err != nil { @@ -556,6 +1260,11 @@ func (c *Client) CreateClinician(ctx context.Context, clinicId ClinicId, body Cr return c.Client.Do(req) } +// DeleteClinician Delete Clinician +// +// Removes a clinician from a clinic. +// +// Corresponds with DELETE /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `DeleteClinician` operationId). func (c *Client) DeleteClinician(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeleteClinicianRequest(c.Server, clinicId, clinicianId) if err != nil { @@ -568,6 +1277,11 @@ func (c *Client) DeleteClinician(ctx context.Context, clinicId ClinicId, clinici return c.Client.Do(req) } +// GetClinician Get Clinician +// +// Retrieve a member of clinic given their user id. +// +// Corresponds with GET /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `GetClinician` operationId). func (c *Client) GetClinician(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetClinicianRequest(c.Server, clinicId, clinicianId) if err != nil { @@ -580,6 +1294,13 @@ func (c *Client) GetClinician(ctx context.Context, clinicId ClinicId, clinicianI return c.Client.Do(req) } +// UpdateClinicianWithBody Update Clinician +// +// Update existing clinician. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `UpdateClinician` operationId). func (c *Client) UpdateClinicianWithBody(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateClinicianRequestWithBody(c.Server, clinicId, clinicianId, contentType, body) if err != nil { @@ -592,6 +1313,13 @@ func (c *Client) UpdateClinicianWithBody(ctx context.Context, clinicId ClinicId, return c.Client.Do(req) } +// UpdateClinician Update Clinician +// +// Update existing clinician. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `UpdateClinician` operationId). func (c *Client) UpdateClinician(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, body UpdateClinicianJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateClinicianRequest(c.Server, clinicId, clinicianId, body) if err != nil { @@ -604,6 +1332,13 @@ func (c *Client) UpdateClinician(ctx context.Context, clinicId ClinicId, clinici return c.Client.Do(req) } +// SyncEHRData Sync EHR Data +// +// An internal endpoint which will push the latest patient summary statistics and PDF reports for all patients who have an active subscription. The actual data is pushed asynchronously. A successful response means that an asynchronous task has been scheduled for each patient of the clinic with an active subscription. +// +// Will return 404 Not Found if the clinic does not have an active EHR connection. +// +// Corresponds with POST /v1/clinics/{clinicId}/ehr/sync (the `SyncEHRData` operationId). func (c *Client) SyncEHRData(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewSyncEHRDataRequest(c.Server, clinicId) if err != nil { @@ -616,6 +1351,11 @@ func (c *Client) SyncEHRData(ctx context.Context, clinicId ClinicId, reqEditors return c.Client.Do(req) } +// DeleteInvitedClinician Delete Invited Clinician +// +// Internal endpoint to delete an invited clinician object. +// +// Corresponds with DELETE /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `DeleteInvitedClinician` operationId). func (c *Client) DeleteInvitedClinician(ctx context.Context, clinicId ClinicId, inviteId InviteId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeleteInvitedClinicianRequest(c.Server, clinicId, inviteId) if err != nil { @@ -628,6 +1368,11 @@ func (c *Client) DeleteInvitedClinician(ctx context.Context, clinicId ClinicId, return c.Client.Do(req) } +// GetInvitedClinician Get Invited Clinician +// +// Internal endpoint to retrieve invited clinician. +// +// Corresponds with GET /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `GetInvitedClinician` operationId). func (c *Client) GetInvitedClinician(ctx context.Context, clinicId ClinicId, inviteId InviteId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetInvitedClinicianRequest(c.Server, clinicId, inviteId) if err != nil { @@ -640,6 +1385,13 @@ func (c *Client) GetInvitedClinician(ctx context.Context, clinicId ClinicId, inv return c.Client.Do(req) } +// AssociateClinicianToUserWithBody Associate Clinician to User +// +// Internal endpoint to associate a clinician to a user. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PATCH /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `AssociateClinicianToUser` operationId). func (c *Client) AssociateClinicianToUserWithBody(ctx context.Context, clinicId ClinicId, inviteId InviteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewAssociateClinicianToUserRequestWithBody(c.Server, clinicId, inviteId, contentType, body) if err != nil { @@ -652,6 +1404,13 @@ func (c *Client) AssociateClinicianToUserWithBody(ctx context.Context, clinicId return c.Client.Do(req) } +// AssociateClinicianToUser Associate Clinician to User +// +// Internal endpoint to associate a clinician to a user. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PATCH /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `AssociateClinicianToUser` operationId). func (c *Client) AssociateClinicianToUser(ctx context.Context, clinicId ClinicId, inviteId InviteId, body AssociateClinicianToUserJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewAssociateClinicianToUserRequest(c.Server, clinicId, inviteId, body) if err != nil { @@ -664,6 +1423,13 @@ func (c *Client) AssociateClinicianToUser(ctx context.Context, clinicId ClinicId return c.Client.Do(req) } +// ListMembershipRestrictions List Membership Restrictions +// +// Returns a list of the membership restrictions that will be evaluated when a user joins a clinic. +// +// Only clinic admins can access this endpoint. +// +// Corresponds with GET /v1/clinics/{clinicId}/membership_restrictions (the `ListMembershipRestrictions` operationId). func (c *Client) ListMembershipRestrictions(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListMembershipRestrictionsRequest(c.Server, clinicId) if err != nil { @@ -676,6 +1442,13 @@ func (c *Client) ListMembershipRestrictions(ctx context.Context, clinicId Clinic return c.Client.Do(req) } +// UpdateMembershipRestrictionsWithBody Update Membership Restrictions +// +// An internal endpoint to update clinic membership restrictions. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/membership_restrictions (the `UpdateMembershipRestrictions` operationId). func (c *Client) UpdateMembershipRestrictionsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateMembershipRestrictionsRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -688,6 +1461,13 @@ func (c *Client) UpdateMembershipRestrictionsWithBody(ctx context.Context, clini return c.Client.Do(req) } +// UpdateMembershipRestrictions Update Membership Restrictions +// +// An internal endpoint to update clinic membership restrictions. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/membership_restrictions (the `UpdateMembershipRestrictions` operationId). func (c *Client) UpdateMembershipRestrictions(ctx context.Context, clinicId ClinicId, body UpdateMembershipRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateMembershipRestrictionsRequest(c.Server, clinicId, body) if err != nil { @@ -700,6 +1480,13 @@ func (c *Client) UpdateMembershipRestrictions(ctx context.Context, clinicId Clin return c.Client.Do(req) } +// MergeClinicWithBody Merge Clinic +// +// # Merges tags, patients, clinicians, invites and share codes of the source clinic +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/merge (the `MergeClinic` operationId). func (c *Client) MergeClinicWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewMergeClinicRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -712,6 +1499,13 @@ func (c *Client) MergeClinicWithBody(ctx context.Context, clinicId ClinicId, con return c.Client.Do(req) } +// MergeClinic Merge Clinic +// +// # Merges tags, patients, clinicians, invites and share codes of the source clinic +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/merge (the `MergeClinic` operationId). func (c *Client) MergeClinic(ctx context.Context, clinicId ClinicId, body MergeClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewMergeClinicRequest(c.Server, clinicId, body) if err != nil { @@ -724,6 +1518,13 @@ func (c *Client) MergeClinic(ctx context.Context, clinicId ClinicId, body MergeC return c.Client.Do(req) } +// TriggerInitialMigrationWithBody Trigger initial migration +// +// Trigger the initial migration. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/migrate (the `TriggerInitialMigration` operationId). func (c *Client) TriggerInitialMigrationWithBody(ctx context.Context, clinicId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewTriggerInitialMigrationRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -736,6 +1537,13 @@ func (c *Client) TriggerInitialMigrationWithBody(ctx context.Context, clinicId s return c.Client.Do(req) } +// TriggerInitialMigration Trigger initial migration +// +// Trigger the initial migration. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/migrate (the `TriggerInitialMigration` operationId). func (c *Client) TriggerInitialMigration(ctx context.Context, clinicId string, body TriggerInitialMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewTriggerInitialMigrationRequest(c.Server, clinicId, body) if err != nil { @@ -748,6 +1556,11 @@ func (c *Client) TriggerInitialMigration(ctx context.Context, clinicId string, b return c.Client.Do(req) } +// ListMigrations Retrieve Migration Status +// +// ListMigrations. +// +// Corresponds with GET /v1/clinics/{clinicId}/migrations (the `ListMigrations` operationId). func (c *Client) ListMigrations(ctx context.Context, clinicId string, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListMigrationsRequest(c.Server, clinicId) if err != nil { @@ -760,6 +1573,13 @@ func (c *Client) ListMigrations(ctx context.Context, clinicId string, reqEditors return c.Client.Do(req) } +// MigrateLegacyClinicianPatientsWithBody Migrate Legacy Clinician Patients +// +// Migrate the patients of a legacy clinic account. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/migrations (the `MigrateLegacyClinicianPatients` operationId). func (c *Client) MigrateLegacyClinicianPatientsWithBody(ctx context.Context, clinicId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewMigrateLegacyClinicianPatientsRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -772,6 +1592,13 @@ func (c *Client) MigrateLegacyClinicianPatientsWithBody(ctx context.Context, cli return c.Client.Do(req) } +// MigrateLegacyClinicianPatients Migrate Legacy Clinician Patients +// +// Migrate the patients of a legacy clinic account. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/migrations (the `MigrateLegacyClinicianPatients` operationId). func (c *Client) MigrateLegacyClinicianPatients(ctx context.Context, clinicId string, body MigrateLegacyClinicianPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewMigrateLegacyClinicianPatientsRequest(c.Server, clinicId, body) if err != nil { @@ -784,6 +1611,11 @@ func (c *Client) MigrateLegacyClinicianPatients(ctx context.Context, clinicId st return c.Client.Do(req) } +// GetMigration Get Migration +// +// Get a migration by user id. +// +// Corresponds with GET /v1/clinics/{clinicId}/migrations/{userId} (the `GetMigration` operationId). func (c *Client) GetMigration(ctx context.Context, clinicId ClinicIdV1, userId UserId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetMigrationRequest(c.Server, clinicId, userId) if err != nil { @@ -796,6 +1628,13 @@ func (c *Client) GetMigration(ctx context.Context, clinicId ClinicIdV1, userId U return c.Client.Do(req) } +// UpdateMigrationWithBody Update Migration +// +// Internal endpoint to update the status of a migration. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PATCH /v1/clinics/{clinicId}/migrations/{userId} (the `UpdateMigration` operationId). func (c *Client) UpdateMigrationWithBody(ctx context.Context, clinicId ClinicIdV1, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateMigrationRequestWithBody(c.Server, clinicId, userId, contentType, body) if err != nil { @@ -808,6 +1647,13 @@ func (c *Client) UpdateMigrationWithBody(ctx context.Context, clinicId ClinicIdV return c.Client.Do(req) } +// UpdateMigration Update Migration +// +// Internal endpoint to update the status of a migration. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PATCH /v1/clinics/{clinicId}/migrations/{userId} (the `UpdateMigration` operationId). func (c *Client) UpdateMigration(ctx context.Context, clinicId ClinicIdV1, userId UserId, body UpdateMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateMigrationRequest(c.Server, clinicId, userId, body) if err != nil { @@ -820,6 +1666,9 @@ func (c *Client) UpdateMigration(ctx context.Context, clinicId ClinicIdV1, userI return c.Client.Do(req) } +// GetPatientCount Get Patient Count +// +// Corresponds with GET /v1/clinics/{clinicId}/patient_count (the `GetPatientCount` operationId). func (c *Client) GetPatientCount(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetPatientCountRequest(c.Server, clinicId) if err != nil { @@ -832,6 +1681,11 @@ func (c *Client) GetPatientCount(ctx context.Context, clinicId ClinicId, reqEdit return c.Client.Do(req) } +// RefreshPatientCount Refresh Patient Count +// +// An internal endpoint to refresh the patient count for the specified clinic. +// +// Corresponds with POST /v1/clinics/{clinicId}/patient_count/refresh (the `RefreshPatientCount` operationId). func (c *Client) RefreshPatientCount(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewRefreshPatientCountRequest(c.Server, clinicId) if err != nil { @@ -844,6 +1698,13 @@ func (c *Client) RefreshPatientCount(ctx context.Context, clinicId ClinicId, req return c.Client.Do(req) } +// CreatePatientTagWithBody Create Patient Tag +// +// Create a new patient tag. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patient_tags (the `CreatePatientTag` operationId). func (c *Client) CreatePatientTagWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreatePatientTagRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -856,6 +1717,13 @@ func (c *Client) CreatePatientTagWithBody(ctx context.Context, clinicId ClinicId return c.Client.Do(req) } +// CreatePatientTag Create Patient Tag +// +// Create a new patient tag. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patient_tags (the `CreatePatientTag` operationId). func (c *Client) CreatePatientTag(ctx context.Context, clinicId ClinicId, body CreatePatientTagJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreatePatientTagRequest(c.Server, clinicId, body) if err != nil { @@ -868,6 +1736,11 @@ func (c *Client) CreatePatientTag(ctx context.Context, clinicId ClinicId, body C return c.Client.Do(req) } +// DeletePatientTag Delete Patient Tag +// +// Removes a patient tag from a clinic and tagged patients. +// +// Corresponds with DELETE /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `DeletePatientTag` operationId). func (c *Client) DeletePatientTag(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeletePatientTagRequest(c.Server, clinicId, patientTagId) if err != nil { @@ -880,6 +1753,13 @@ func (c *Client) DeletePatientTag(ctx context.Context, clinicId ClinicId, patien return c.Client.Do(req) } +// UpdatePatientTagWithBody Update Patient Tag +// +// Update existing patient tag. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `UpdatePatientTag` operationId). func (c *Client) UpdatePatientTagWithBody(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientTagRequestWithBody(c.Server, clinicId, patientTagId, contentType, body) if err != nil { @@ -892,6 +1772,13 @@ func (c *Client) UpdatePatientTagWithBody(ctx context.Context, clinicId ClinicId return c.Client.Do(req) } +// UpdatePatientTag Update Patient Tag +// +// Update existing patient tag. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `UpdatePatientTag` operationId). func (c *Client) UpdatePatientTag(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body UpdatePatientTagJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientTagRequest(c.Server, clinicId, patientTagId, body) if err != nil { @@ -904,6 +1791,11 @@ func (c *Client) UpdatePatientTag(ctx context.Context, clinicId ClinicId, patien return c.Client.Do(req) } +// ConvertPatientTagToSite Convert Patient Tag to Site +// +// Convert a patient tag to a site. +// +// Corresponds with POST /v1/clinics/{clinicId}/patient_tags/{patientTagId}/site (the `ConvertPatientTagToSite` operationId). func (c *Client) ConvertPatientTagToSite(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewConvertPatientTagToSiteRequest(c.Server, clinicId, patientTagId) if err != nil { @@ -916,6 +1808,11 @@ func (c *Client) ConvertPatientTagToSite(ctx context.Context, clinicId ClinicId, return c.Client.Do(req) } +// ListPatients List Patients +// +// Retrieve a list of patients of a clinic. +// +// Corresponds with GET /v1/clinics/{clinicId}/patients (the `ListPatients` operationId). func (c *Client) ListPatients(ctx context.Context, clinicId ClinicId, params *ListPatientsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListPatientsRequest(c.Server, clinicId, params) if err != nil { @@ -928,6 +1825,13 @@ func (c *Client) ListPatients(ctx context.Context, clinicId ClinicId, params *Li return c.Client.Do(req) } +// CreatePatientAccountWithBody Create Patient Account +// +// Create a custodial account for a patient. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients (the `CreatePatientAccount` operationId). func (c *Client) CreatePatientAccountWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreatePatientAccountRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -940,6 +1844,13 @@ func (c *Client) CreatePatientAccountWithBody(ctx context.Context, clinicId Clin return c.Client.Do(req) } +// CreatePatientAccount Create Patient Account +// +// Create a custodial account for a patient. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients (the `CreatePatientAccount` operationId). func (c *Client) CreatePatientAccount(ctx context.Context, clinicId ClinicId, body CreatePatientAccountJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreatePatientAccountRequest(c.Server, clinicId, body) if err != nil { @@ -952,6 +1863,13 @@ func (c *Client) CreatePatientAccount(ctx context.Context, clinicId ClinicId, bo return c.Client.Do(req) } +// AssignPatientTagToClinicPatientsWithBody Assign Patient Tag To Clinic Patients +// +// Assign a patient tag to a subset of clinic patients. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/assign_tag/{patientTagId} (the `AssignPatientTagToClinicPatients` operationId). func (c *Client) AssignPatientTagToClinicPatientsWithBody(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewAssignPatientTagToClinicPatientsRequestWithBody(c.Server, clinicId, patientTagId, contentType, body) if err != nil { @@ -964,6 +1882,13 @@ func (c *Client) AssignPatientTagToClinicPatientsWithBody(ctx context.Context, c return c.Client.Do(req) } +// AssignPatientTagToClinicPatients Assign Patient Tag To Clinic Patients +// +// Assign a patient tag to a subset of clinic patients. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/assign_tag/{patientTagId} (the `AssignPatientTagToClinicPatients` operationId). func (c *Client) AssignPatientTagToClinicPatients(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body AssignPatientTagToClinicPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewAssignPatientTagToClinicPatientsRequest(c.Server, clinicId, patientTagId, body) if err != nil { @@ -976,6 +1901,13 @@ func (c *Client) AssignPatientTagToClinicPatients(ctx context.Context, clinicId return c.Client.Do(req) } +// DeletePatientTagFromClinicPatientsWithBody Delete Patient Tag From Clinic Patients +// +// Delete a patient tag from all or a subset of clinic patients. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/delete_tag/{patientTagId} (the `DeletePatientTagFromClinicPatients` operationId). func (c *Client) DeletePatientTagFromClinicPatientsWithBody(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeletePatientTagFromClinicPatientsRequestWithBody(c.Server, clinicId, patientTagId, contentType, body) if err != nil { @@ -988,6 +1920,13 @@ func (c *Client) DeletePatientTagFromClinicPatientsWithBody(ctx context.Context, return c.Client.Do(req) } +// DeletePatientTagFromClinicPatients Delete Patient Tag From Clinic Patients +// +// Delete a patient tag from all or a subset of clinic patients. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/delete_tag/{patientTagId} (the `DeletePatientTagFromClinicPatients` operationId). func (c *Client) DeletePatientTagFromClinicPatients(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body DeletePatientTagFromClinicPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeletePatientTagFromClinicPatientsRequest(c.Server, clinicId, patientTagId, body) if err != nil { @@ -1000,6 +1939,11 @@ func (c *Client) DeletePatientTagFromClinicPatients(ctx context.Context, clinicI return c.Client.Do(req) } +// DeletePatient Delete Patient +// +// Deletes patient from a clinic. Requires the request to be made by a clinic admin or the currently authenticated user id to match the patient id. +// +// Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId} (the `DeletePatient` operationId). func (c *Client) DeletePatient(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeletePatientRequest(c.Server, clinicId, patientId) if err != nil { @@ -1012,6 +1956,11 @@ func (c *Client) DeletePatient(ctx context.Context, clinicId ClinicId, patientId return c.Client.Do(req) } +// GetPatient Get Patient +// +// Retrieve a patient of clinic given their user id. +// +// Corresponds with GET /v1/clinics/{clinicId}/patients/{patientId} (the `GetPatient` operationId). func (c *Client) GetPatient(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetPatientRequest(c.Server, clinicId, patientId) if err != nil { @@ -1024,6 +1973,13 @@ func (c *Client) GetPatient(ctx context.Context, clinicId ClinicId, patientId Pa return c.Client.Do(req) } +// CreatePatientFromUserWithBody Create Patient from Existing User +// +// Internal endpoint for creating a patient from an existing user. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId} (the `CreatePatientFromUser` operationId). func (c *Client) CreatePatientFromUserWithBody(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreatePatientFromUserRequestWithBody(c.Server, clinicId, patientId, contentType, body) if err != nil { @@ -1036,6 +1992,13 @@ func (c *Client) CreatePatientFromUserWithBody(ctx context.Context, clinicId Cli return c.Client.Do(req) } +// CreatePatientFromUser Create Patient from Existing User +// +// Internal endpoint for creating a patient from an existing user. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId} (the `CreatePatientFromUser` operationId). func (c *Client) CreatePatientFromUser(ctx context.Context, clinicId ClinicId, patientId PatientId, body CreatePatientFromUserJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreatePatientFromUserRequest(c.Server, clinicId, patientId, body) if err != nil { @@ -1048,6 +2011,13 @@ func (c *Client) CreatePatientFromUser(ctx context.Context, clinicId ClinicId, p return c.Client.Do(req) } +// UpdatePatientWithBody Update Patient +// +// Update existing patient of a clinic. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId} (the `UpdatePatient` operationId). func (c *Client) UpdatePatientWithBody(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientRequestWithBody(c.Server, clinicId, patientId, contentType, body) if err != nil { @@ -1060,6 +2030,13 @@ func (c *Client) UpdatePatientWithBody(ctx context.Context, clinicId ClinicId, p return c.Client.Do(req) } +// UpdatePatient Update Patient +// +// Update existing patient of a clinic. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId} (the `UpdatePatient` operationId). func (c *Client) UpdatePatient(ctx context.Context, clinicId ClinicId, patientId PatientId, body UpdatePatientJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientRequest(c.Server, clinicId, patientId, body) if err != nil { @@ -1072,6 +2049,11 @@ func (c *Client) UpdatePatient(ctx context.Context, clinicId ClinicId, patientId return c.Client.Do(req) } +// ConnectProvider Connect Provider +// +// Send a new request to the patient to connect a data provider. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId}/connect/{providerId} (the `ConnectProvider` operationId). func (c *Client) ConnectProvider(ctx context.Context, clinicId ClinicId, patientId PatientId, providerId ProviderId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewConnectProviderRequest(c.Server, clinicId, patientId, providerId) if err != nil { @@ -1084,6 +2066,15 @@ func (c *Client) ConnectProvider(ctx context.Context, clinicId ClinicId, patient return c.Client.Do(req) } +// UpdatePatientPermissionsWithBody Update Patient Permissions +// +// Update permissions that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. +// +// Only patients can change the permissions. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/permissions (the `UpdatePatientPermissions` operationId). func (c *Client) UpdatePatientPermissionsWithBody(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientPermissionsRequestWithBody(c.Server, clinicId, patientId, contentType, body) if err != nil { @@ -1096,6 +2087,15 @@ func (c *Client) UpdatePatientPermissionsWithBody(ctx context.Context, clinicId return c.Client.Do(req) } +// UpdatePatientPermissions Update Patient Permissions +// +// Update permissions that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. +// +// Only patients can change the permissions. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/permissions (the `UpdatePatientPermissions` operationId). func (c *Client) UpdatePatientPermissions(ctx context.Context, clinicId ClinicId, patientId PatientId, body UpdatePatientPermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientPermissionsRequest(c.Server, clinicId, patientId, body) if err != nil { @@ -1108,6 +2108,13 @@ func (c *Client) UpdatePatientPermissions(ctx context.Context, clinicId ClinicId return c.Client.Do(req) } +// DeletePatientPermission Delete Patient Permission +// +// Remove a single permission that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. +// +// Only patients can change the permissions. +// +// Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId}/permissions/{permission} (the `DeletePatientPermission` operationId). func (c *Client) DeletePatientPermission(ctx context.Context, clinicId ClinicId, patientId PatientId, permission string, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeletePatientPermissionRequest(c.Server, clinicId, patientId, permission) if err != nil { @@ -1120,6 +2127,11 @@ func (c *Client) DeletePatientPermission(ctx context.Context, clinicId ClinicId, return c.Client.Do(req) } +// DeletePatientReviews Delete Patient Reviews +// +// Revert the patient's last reviewed date to the previous set. +// +// Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId}/reviews (the `DeletePatientReviews` operationId). func (c *Client) DeletePatientReviews(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeletePatientReviewsRequest(c.Server, clinicId, patientId) if err != nil { @@ -1132,6 +2144,11 @@ func (c *Client) DeletePatientReviews(ctx context.Context, clinicId ClinicId, pa return c.Client.Do(req) } +// UpdatePatientReviews Update Patient Reviews +// +// Update the patient's last reviewed date and clinician ID. +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/reviews (the `UpdatePatientReviews` operationId). func (c *Client) UpdatePatientReviews(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientReviewsRequest(c.Server, clinicId, patientId) if err != nil { @@ -1144,6 +2161,11 @@ func (c *Client) UpdatePatientReviews(ctx context.Context, clinicId ClinicId, pa return c.Client.Do(req) } +// SendUploadReminder Send Upload Reminder +// +// Sends upload reminder to the patient. +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId}/upload_reminder (the `SendUploadReminder` operationId). func (c *Client) SendUploadReminder(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewSendUploadReminderRequest(c.Server, clinicId, patientId) if err != nil { @@ -1156,6 +2178,13 @@ func (c *Client) SendUploadReminder(ctx context.Context, clinicId ClinicId, pati return c.Client.Do(req) } +// GenerateMergeReportWithBody Generate Clinic Merge Report +// +// Generates a spreadsheet report for merging two clinics. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/reports/merge (the `GenerateMergeReport` operationId). func (c *Client) GenerateMergeReportWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGenerateMergeReportRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -1168,6 +2197,13 @@ func (c *Client) GenerateMergeReportWithBody(ctx context.Context, clinicId Clini return c.Client.Do(req) } +// GenerateMergeReport Generate Clinic Merge Report +// +// Generates a spreadsheet report for merging two clinics. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/reports/merge (the `GenerateMergeReport` operationId). func (c *Client) GenerateMergeReport(ctx context.Context, clinicId ClinicId, body GenerateMergeReportJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGenerateMergeReportRequest(c.Server, clinicId, body) if err != nil { @@ -1180,6 +2216,13 @@ func (c *Client) GenerateMergeReport(ctx context.Context, clinicId ClinicId, bod return c.Client.Do(req) } +// AddServiceAccountWithBody Add Service Account +// +// Adds authentication client's service account as a new clinician member. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/service_accounts (the `AddServiceAccount` operationId). func (c *Client) AddServiceAccountWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewAddServiceAccountRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -1192,6 +2235,13 @@ func (c *Client) AddServiceAccountWithBody(ctx context.Context, clinicId ClinicI return c.Client.Do(req) } +// AddServiceAccount Add Service Account +// +// Adds authentication client's service account as a new clinician member. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/service_accounts (the `AddServiceAccount` operationId). func (c *Client) AddServiceAccount(ctx context.Context, clinicId ClinicId, body AddServiceAccountJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewAddServiceAccountRequest(c.Server, clinicId, body) if err != nil { @@ -1204,6 +2254,11 @@ func (c *Client) AddServiceAccount(ctx context.Context, clinicId ClinicId, body return c.Client.Do(req) } +// GetEHRSettings Get EHR Settings +// +// Returns EHR related settings. +// +// Corresponds with GET /v1/clinics/{clinicId}/settings/ehr (the `GetEHRSettings` operationId). func (c *Client) GetEHRSettings(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetEHRSettingsRequest(c.Server, clinicId) if err != nil { @@ -1216,6 +2271,13 @@ func (c *Client) GetEHRSettings(ctx context.Context, clinicId ClinicId, reqEdito return c.Client.Do(req) } +// UpdateEHRSettingsWithBody Update EHR Settings +// +// Update EHR related settings for a clinic. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/ehr (the `UpdateEHRSettings` operationId). func (c *Client) UpdateEHRSettingsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateEHRSettingsRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -1228,6 +2290,13 @@ func (c *Client) UpdateEHRSettingsWithBody(ctx context.Context, clinicId ClinicI return c.Client.Do(req) } +// UpdateEHRSettings Update EHR Settings +// +// Update EHR related settings for a clinic. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/ehr (the `UpdateEHRSettings` operationId). func (c *Client) UpdateEHRSettings(ctx context.Context, clinicId ClinicId, body UpdateEHRSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateEHRSettingsRequest(c.Server, clinicId, body) if err != nil { @@ -1240,6 +2309,11 @@ func (c *Client) UpdateEHRSettings(ctx context.Context, clinicId ClinicId, body return c.Client.Do(req) } +// GetMRNSettings Get MRN Settings +// +// Retrieve MRN settings. +// +// Corresponds with GET /v1/clinics/{clinicId}/settings/mrn (the `GetMRNSettings` operationId). func (c *Client) GetMRNSettings(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetMRNSettingsRequest(c.Server, clinicId) if err != nil { @@ -1252,6 +2326,11 @@ func (c *Client) GetMRNSettings(ctx context.Context, clinicId ClinicId, reqEdito return c.Client.Do(req) } +// UpdateMRNSettingsWithBody Update MRN Settings +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/mrn (the `UpdateMRNSettings` operationId). func (c *Client) UpdateMRNSettingsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateMRNSettingsRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -1264,6 +2343,11 @@ func (c *Client) UpdateMRNSettingsWithBody(ctx context.Context, clinicId ClinicI return c.Client.Do(req) } +// UpdateMRNSettings Update MRN Settings +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/mrn (the `UpdateMRNSettings` operationId). func (c *Client) UpdateMRNSettings(ctx context.Context, clinicId ClinicId, body UpdateMRNSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateMRNSettingsRequest(c.Server, clinicId, body) if err != nil { @@ -1276,6 +2360,11 @@ func (c *Client) UpdateMRNSettings(ctx context.Context, clinicId ClinicId, body return c.Client.Do(req) } +// GetPatientCountSettings Get Patient Count Settings +// +// Get Patient Count settings. +// +// Corresponds with GET /v1/clinics/{clinicId}/settings/patient_count (the `GetPatientCountSettings` operationId). func (c *Client) GetPatientCountSettings(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetPatientCountSettingsRequest(c.Server, clinicId) if err != nil { @@ -1288,6 +2377,11 @@ func (c *Client) GetPatientCountSettings(ctx context.Context, clinicId ClinicId, return c.Client.Do(req) } +// UpdatePatientCountSettingsWithBody Update Patient Count Settings +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/patient_count (the `UpdatePatientCountSettings` operationId). func (c *Client) UpdatePatientCountSettingsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientCountSettingsRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -1300,6 +2394,11 @@ func (c *Client) UpdatePatientCountSettingsWithBody(ctx context.Context, clinicI return c.Client.Do(req) } +// UpdatePatientCountSettings Update Patient Count Settings +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/patient_count (the `UpdatePatientCountSettings` operationId). func (c *Client) UpdatePatientCountSettings(ctx context.Context, clinicId ClinicId, body UpdatePatientCountSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientCountSettingsRequest(c.Server, clinicId, body) if err != nil { @@ -1312,6 +2411,11 @@ func (c *Client) UpdatePatientCountSettings(ctx context.Context, clinicId Clinic return c.Client.Do(req) } +// CreateSiteWithBody Create a Site +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/sites (the `CreateSite` operationId). func (c *Client) CreateSiteWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreateSiteRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -1324,6 +2428,11 @@ func (c *Client) CreateSiteWithBody(ctx context.Context, clinicId ClinicId, cont return c.Client.Do(req) } +// CreateSite Create a Site +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/sites (the `CreateSite` operationId). func (c *Client) CreateSite(ctx context.Context, clinicId ClinicId, body CreateSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewCreateSiteRequest(c.Server, clinicId, body) if err != nil { @@ -1336,6 +2445,9 @@ func (c *Client) CreateSite(ctx context.Context, clinicId ClinicId, body CreateS return c.Client.Do(req) } +// DeleteSite Delete a Site +// +// Corresponds with DELETE /v1/clinics/{clinicId}/sites/{siteId} (the `DeleteSite` operationId). func (c *Client) DeleteSite(ctx context.Context, clinicId ClinicId, siteId SiteId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeleteSiteRequest(c.Server, clinicId, siteId) if err != nil { @@ -1348,6 +2460,11 @@ func (c *Client) DeleteSite(ctx context.Context, clinicId ClinicId, siteId SiteI return c.Client.Do(req) } +// UpdateSiteWithBody Update a Site +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/sites/{siteId} (the `UpdateSite` operationId). func (c *Client) UpdateSiteWithBody(ctx context.Context, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateSiteRequestWithBody(c.Server, clinicId, siteId, contentType, body) if err != nil { @@ -1360,6 +2477,11 @@ func (c *Client) UpdateSiteWithBody(ctx context.Context, clinicId ClinicId, site return c.Client.Do(req) } +// UpdateSite Update a Site +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/clinics/{clinicId}/sites/{siteId} (the `UpdateSite` operationId). func (c *Client) UpdateSite(ctx context.Context, clinicId ClinicId, siteId SiteId, body UpdateSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateSiteRequest(c.Server, clinicId, siteId, body) if err != nil { @@ -1372,6 +2494,13 @@ func (c *Client) UpdateSite(ctx context.Context, clinicId ClinicId, siteId SiteI return c.Client.Do(req) } +// MergeSiteWithBody Merge two sites +// +// Merge a site, identified in the request body, into the site identified in the URL. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/sites/{siteId}/merge (the `MergeSite` operationId). func (c *Client) MergeSiteWithBody(ctx context.Context, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewMergeSiteRequestWithBody(c.Server, clinicId, siteId, contentType, body) if err != nil { @@ -1384,6 +2513,13 @@ func (c *Client) MergeSiteWithBody(ctx context.Context, clinicId ClinicId, siteI return c.Client.Do(req) } +// MergeSite Merge two sites +// +// Merge a site, identified in the request body, into the site identified in the URL. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/sites/{siteId}/merge (the `MergeSite` operationId). func (c *Client) MergeSite(ctx context.Context, clinicId ClinicId, siteId SiteId, body MergeSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewMergeSiteRequest(c.Server, clinicId, siteId, body) if err != nil { @@ -1396,6 +2532,13 @@ func (c *Client) MergeSite(ctx context.Context, clinicId ClinicId, siteId SiteId return c.Client.Do(req) } +// UpdateSuppressedNotificationsWithBody Update Suppressed Notifications +// +// Endpoint to update clinic suppressed notifications. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/suppressed_notifications (the `UpdateSuppressedNotifications` operationId). func (c *Client) UpdateSuppressedNotificationsWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateSuppressedNotificationsRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -1408,6 +2551,13 @@ func (c *Client) UpdateSuppressedNotificationsWithBody(ctx context.Context, clin return c.Client.Do(req) } +// UpdateSuppressedNotifications Update Suppressed Notifications +// +// Endpoint to update clinic suppressed notifications. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/suppressed_notifications (the `UpdateSuppressedNotifications` operationId). func (c *Client) UpdateSuppressedNotifications(ctx context.Context, clinicId ClinicId, body UpdateSuppressedNotificationsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateSuppressedNotificationsRequest(c.Server, clinicId, body) if err != nil { @@ -1420,6 +2570,11 @@ func (c *Client) UpdateSuppressedNotifications(ctx context.Context, clinicId Cli return c.Client.Do(req) } +// TideReport View TIDE Report +// +// Retrieve a report of patients with tags by TIDE categories. +// +// Corresponds with GET /v1/clinics/{clinicId}/tide_report (the `TideReport` operationId). func (c *Client) TideReport(ctx context.Context, clinicId ClinicId, params *TideReportParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewTideReportRequest(c.Server, clinicId, params) if err != nil { @@ -1432,6 +2587,13 @@ func (c *Client) TideReport(ctx context.Context, clinicId ClinicId, params *Tide return c.Client.Do(req) } +// UpdateTierWithBody Update Tier +// +// Internal endpoint to update clinic service tier. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/tier (the `UpdateTier` operationId). func (c *Client) UpdateTierWithBody(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateTierRequestWithBody(c.Server, clinicId, contentType, body) if err != nil { @@ -1444,6 +2606,13 @@ func (c *Client) UpdateTierWithBody(ctx context.Context, clinicId ClinicId, cont return c.Client.Do(req) } +// UpdateTier Update Tier +// +// Internal endpoint to update clinic service tier. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/clinics/{clinicId}/tier (the `UpdateTier` operationId). func (c *Client) UpdateTier(ctx context.Context, clinicId ClinicId, body UpdateTierJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateTierRequest(c.Server, clinicId, body) if err != nil { @@ -1456,6 +2625,11 @@ func (c *Client) UpdateTier(ctx context.Context, clinicId ClinicId, body UpdateT return c.Client.Do(req) } +// FindPatients Find Patients +// +// Returns all patients to which the clinician has access to. +// +// Corresponds with GET /v1/patients (the `FindPatients` operationId). func (c *Client) FindPatients(ctx context.Context, params *FindPatientsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewFindPatientsRequest(c.Server, params) if err != nil { @@ -1468,6 +2642,13 @@ func (c *Client) FindPatients(ctx context.Context, params *FindPatientsParams, r return c.Client.Do(req) } +// SyncEHRDataForPatient Sync EHR Data for Patient +// +// An internal endpoint which will push the latest patient summary statistics and PDF reports to all EHR enabled clinics for which the patient has an active subscription. The actual data is pushed asynchronously. A successful response means that an asynchronous task has been scheduled. +// +// A 404 response will be returned if no active subscriptions can be found for given patient. +// +// Corresponds with POST /v1/patients/{patientId}/ehr/sync (the `SyncEHRDataForPatient` operationId). func (c *Client) SyncEHRDataForPatient(ctx context.Context, patientId PatientId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewSyncEHRDataForPatientRequest(c.Server, patientId) if err != nil { @@ -1480,6 +2661,13 @@ func (c *Client) SyncEHRDataForPatient(ctx context.Context, patientId PatientId, return c.Client.Do(req) } +// UpdatePatientSummaryWithBody UpdatePatientSummary +// +// Internal endpoint which updates the summary in all patient records which match the user id. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/patients/{patientId}/summary (the `UpdatePatientSummary` operationId). func (c *Client) UpdatePatientSummaryWithBody(ctx context.Context, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientSummaryRequestWithBody(c.Server, patientId, contentType, body) if err != nil { @@ -1492,6 +2680,13 @@ func (c *Client) UpdatePatientSummaryWithBody(ctx context.Context, patientId Pat return c.Client.Do(req) } +// UpdatePatientSummary UpdatePatientSummary +// +// Internal endpoint which updates the summary in all patient records which match the user id. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/patients/{patientId}/summary (the `UpdatePatientSummary` operationId). func (c *Client) UpdatePatientSummary(ctx context.Context, patientId PatientId, body UpdatePatientSummaryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientSummaryRequest(c.Server, patientId, body) if err != nil { @@ -1504,6 +2699,11 @@ func (c *Client) UpdatePatientSummary(ctx context.Context, patientId PatientId, return c.Client.Do(req) } +// ListClinicsForPatient List Clinics for Patient +// +// Get all clinics a user is a patient of. +// +// Corresponds with GET /v1/patients/{userId}/clinics (the `ListClinicsForPatient` operationId). func (c *Client) ListClinicsForPatient(ctx context.Context, userId UserId, params *ListClinicsForPatientParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListClinicsForPatientRequest(c.Server, userId, params) if err != nil { @@ -1516,6 +2716,13 @@ func (c *Client) ListClinicsForPatient(ctx context.Context, userId UserId, param return c.Client.Do(req) } +// UpdatePatientDataSourcesWithBody Create or update a data source for a patient +// +// Internal endpoint to upsert patient data source provider details in all their associated clinic records. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /v1/patients/{userId}/data_sources (the `UpdatePatientDataSources` operationId). func (c *Client) UpdatePatientDataSourcesWithBody(ctx context.Context, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientDataSourcesRequestWithBody(c.Server, userId, contentType, body) if err != nil { @@ -1528,6 +2735,13 @@ func (c *Client) UpdatePatientDataSourcesWithBody(ctx context.Context, userId Us return c.Client.Do(req) } +// UpdatePatientDataSources Create or update a data source for a patient +// +// Internal endpoint to upsert patient data source provider details in all their associated clinic records. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /v1/patients/{userId}/data_sources (the `UpdatePatientDataSources` operationId). func (c *Client) UpdatePatientDataSources(ctx context.Context, userId UserId, body UpdatePatientDataSourcesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdatePatientDataSourcesRequest(c.Server, userId, body) if err != nil { @@ -1540,6 +2754,13 @@ func (c *Client) UpdatePatientDataSources(ctx context.Context, userId UserId, bo return c.Client.Do(req) } +// ProcessEHRMessageWithBody Redox EHR Endpoint +// +// An endpoint for incoming Redox messages. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/redox (the `ProcessEHRMessage` operationId). func (c *Client) ProcessEHRMessageWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewProcessEHRMessageRequestWithBody(c.Server, contentType, body) if err != nil { @@ -1552,6 +2773,13 @@ func (c *Client) ProcessEHRMessageWithBody(ctx context.Context, contentType stri return c.Client.Do(req) } +// ProcessEHRMessage Redox EHR Endpoint +// +// An endpoint for incoming Redox messages. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/redox (the `ProcessEHRMessage` operationId). func (c *Client) ProcessEHRMessage(ctx context.Context, body ProcessEHRMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewProcessEHRMessageRequest(c.Server, body) if err != nil { @@ -1564,7 +2792,20 @@ func (c *Client) ProcessEHRMessage(ctx context.Context, body ProcessEHRMessageJS return c.Client.Do(req) } -func (c *Client) MatchClinicAndPatientWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { +// MatchClinicAndPatientWithBody Match Clinic and Patient +// +// An internal endpoint used for matching an EHR request (e.g. NewOrder) to a clinic and patient. +// +// If a clinic with the provided details cannot be found, the service will return `404 Not Found` and will not attempt to match patients to the provided details. +// +// Due to lack of uniqueness constraints on the MRN and DOB fields it's possible that multiple patient records match the provided attributes. All results will be returned in the response and it's the calling services responsiblity to determine how to handle duplicates records. +// +// If a unique match is found, the patient matching criteria will be added to the patient record for future use (e.g. pushing data on a schedule). +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/redox/match (the `MatchClinicAndPatient` operationId). +func (c *Client) MatchClinicAndPatientWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewMatchClinicAndPatientRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err @@ -1576,6 +2817,19 @@ func (c *Client) MatchClinicAndPatientWithBody(ctx context.Context, contentType return c.Client.Do(req) } +// MatchClinicAndPatient Match Clinic and Patient +// +// An internal endpoint used for matching an EHR request (e.g. NewOrder) to a clinic and patient. +// +// If a clinic with the provided details cannot be found, the service will return `404 Not Found` and will not attempt to match patients to the provided details. +// +// Due to lack of uniqueness constraints on the MRN and DOB fields it's possible that multiple patient records match the provided attributes. All results will be returned in the response and it's the calling services responsiblity to determine how to handle duplicates records. +// +// If a unique match is found, the patient matching criteria will be added to the patient record for future use (e.g. pushing data on a schedule). +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/redox/match (the `MatchClinicAndPatient` operationId). func (c *Client) MatchClinicAndPatient(ctx context.Context, body MatchClinicAndPatientJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewMatchClinicAndPatientRequest(c.Server, body) if err != nil { @@ -1588,6 +2842,11 @@ func (c *Client) MatchClinicAndPatient(ctx context.Context, body MatchClinicAndP return c.Client.Do(req) } +// VerifyEndpoint Redox Verify Endpoint +// +// Internal endpoint for Redox challenge verification. +// +// Corresponds with POST /v1/redox/verify (the `VerifyEndpoint` operationId). func (c *Client) VerifyEndpoint(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewVerifyEndpointRequest(c.Server) if err != nil { @@ -1600,6 +2859,11 @@ func (c *Client) VerifyEndpoint(ctx context.Context, reqEditors ...RequestEditor return c.Client.Do(req) } +// DeletePatientSummary DeletePatientSummary +// +// Internal endpoint which deletes the summary in all patient records which match the summary id. +// +// Corresponds with DELETE /v1/summaries/{summaryId}/clinics (the `DeletePatientSummary` operationId). func (c *Client) DeletePatientSummary(ctx context.Context, summaryId SummaryId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeletePatientSummaryRequest(c.Server, summaryId) if err != nil { @@ -1612,6 +2876,21 @@ func (c *Client) DeletePatientSummary(ctx context.Context, summaryId SummaryId, return c.Client.Do(req) } +// DeleteUserFromClinics Remove User from Clinics +// +// Internal endpoint that deletes any user associations with clinics they are a patient or a member of. +// +// If the user is a patient of a clinic the service will delete the patient-clinic relationship. +// +// If the user is one of many admins of any clinic the service will delete the clinician-clinic relationship. +// +// If the user is the only admin of a clinic the service will: +// - delete the clinician record, leaving the clinic without an admin +// - delete all non-custodial patient relationships of the clinic +// - keep all custodian relationships, allowing users to claim custodial accounts with an email +// - keep all custodian accounts and data even if they can’t be claimed when there isn’t an email address associated with the account +// +// Corresponds with DELETE /v1/users/{userId}/clinics (the `DeleteUserFromClinics` operationId). func (c *Client) DeleteUserFromClinics(ctx context.Context, userId UserId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeleteUserFromClinicsRequest(c.Server, userId) if err != nil { @@ -1624,6 +2903,13 @@ func (c *Client) DeleteUserFromClinics(ctx context.Context, userId UserId, reqEd return c.Client.Do(req) } +// UpdateClinicUserDetailsWithBody Update User Details +// +// Internal endpoint to update clinician details. In the future it may be extended to support patient updates. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /v1/users/{userId}/clinics (the `UpdateClinicUserDetails` operationId). func (c *Client) UpdateClinicUserDetailsWithBody(ctx context.Context, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateClinicUserDetailsRequestWithBody(c.Server, userId, contentType, body) if err != nil { @@ -1636,6 +2922,13 @@ func (c *Client) UpdateClinicUserDetailsWithBody(ctx context.Context, userId Use return c.Client.Do(req) } +// UpdateClinicUserDetails Update User Details +// +// Internal endpoint to update clinician details. In the future it may be extended to support patient updates. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /v1/users/{userId}/clinics (the `UpdateClinicUserDetails` operationId). func (c *Client) UpdateClinicUserDetails(ctx context.Context, userId UserId, body UpdateClinicUserDetailsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUpdateClinicUserDetailsRequest(c.Server, userId, body) if err != nil { @@ -1648,6 +2941,11 @@ func (c *Client) UpdateClinicUserDetails(ctx context.Context, userId UserId, bod return c.Client.Do(req) } +// XealthNotification Notification Webhook +// +// Notification webhook. +// +// Corresponds with POST /v1/xealth/notification (the `XealthNotification` operationId). func (c *Client) XealthNotification(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewXealthNotificationRequest(c.Server) if err != nil { @@ -1660,6 +2958,13 @@ func (c *Client) XealthNotification(ctx context.Context, reqEditors ...RequestEd return c.Client.Do(req) } +// XealthPreorder Preorder Form Webhook +// +// The Preorder endpoint can be invoked when a care provider attempts to place an order manually within the EHR. Subscribers can use Preorder Forms to present additional information to, or receive input from, a care provider before an order can be placed. Preorder Forms can also be implemented so as to prevent an order from being placed entirely. +// +// https://sandbox.xealth.io/app/api-docs#tag/Digital-Care/paths/preorder/post +// +// Corresponds with POST /v1/xealth/preorder (the `XealthPreorder` operationId). func (c *Client) XealthPreorder(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewXealthPreorderRequest(c.Server) if err != nil { @@ -1672,6 +2977,11 @@ func (c *Client) XealthPreorder(ctx context.Context, reqEditors ...RequestEditor return c.Client.Do(req) } +// XealthGetProgramUrl Get Program Url +// +// Get program URL. +// +// Corresponds with PUT /v1/xealth/program (the `XealthGetProgramUrl` operationId). func (c *Client) XealthGetProgramUrl(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewXealthGetProgramUrlRequest(c.Server) if err != nil { @@ -1684,6 +2994,11 @@ func (c *Client) XealthGetProgramUrl(ctx context.Context, reqEditors ...RequestE return c.Client.Do(req) } +// XealthGetPrograms Get Programs +// +// Get programs. +// +// Corresponds with PUT /v1/xealth/programs (the `XealthGetPrograms` operationId). func (c *Client) XealthGetPrograms(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewXealthGetProgramsRequest(c.Server) if err != nil { @@ -1696,6 +3011,11 @@ func (c *Client) XealthGetPrograms(ctx context.Context, reqEditors ...RequestEdi return c.Client.Do(req) } +// ViewPDFReport View PDF Report +// +// View PDF report for a given patient of a clinic. +// +// Corresponds with GET /v1/xealth/report/web/viewer.html (the `ViewPDFReport` operationId). func (c *Client) ViewPDFReport(ctx context.Context, params *ViewPDFReportParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewViewPDFReportRequest(c.Server, params) if err != nil { @@ -1708,7 +3028,7 @@ func (c *Client) ViewPDFReport(ctx context.Context, params *ViewPDFReportParams, return c.Client.Do(req) } -// NewListAllCliniciansRequest generates requests for ListAllClinicians +// NewListAllCliniciansRequest constructs an http.Request for the ListAllClinicians method func NewListAllCliniciansRequest(server string, params *ListAllCliniciansParams) (*http.Request, error) { var err error @@ -1728,19 +3048,21 @@ func NewListAllCliniciansRequest(server string, params *ListAllCliniciansParams) } if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string if params.Offset != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "offset", runtime.ParamLocationQuery, *params.Offset); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", *params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -1748,15 +3070,11 @@ func NewListAllCliniciansRequest(server string, params *ListAllCliniciansParams) if params.Limit != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -1764,15 +3082,11 @@ func NewListAllCliniciansRequest(server string, params *ListAllCliniciansParams) if params.CreatedTimeStart != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "createdTimeStart", runtime.ParamLocationQuery, *params.CreatedTimeStart); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "createdTimeStart", *params.CreatedTimeStart, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -1780,24 +3094,23 @@ func NewListAllCliniciansRequest(server string, params *ListAllCliniciansParams) if params.CreatedTimeEnd != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "createdTimeEnd", runtime.ParamLocationQuery, *params.CreatedTimeEnd); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "createdTimeEnd", *params.CreatedTimeEnd, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } } - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -1805,13 +3118,13 @@ func NewListAllCliniciansRequest(server string, params *ListAllCliniciansParams) return req, nil } -// NewListClinicsForClinicianRequest generates requests for ListClinicsForClinician +// NewListClinicsForClinicianRequest constructs an http.Request for the ListClinicsForClinician method func NewListClinicsForClinicianRequest(server string, userId UserId, params *ListClinicsForClinicianParams) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "userId", runtime.ParamLocationPath, userId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "userId", userId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -1832,19 +3145,21 @@ func NewListClinicsForClinicianRequest(server string, userId UserId, params *Lis } if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string if params.Offset != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "offset", runtime.ParamLocationQuery, *params.Offset); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", *params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -1852,24 +3167,23 @@ func NewListClinicsForClinicianRequest(server string, userId UserId, params *Lis if params.Limit != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } } - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -1877,13 +3191,13 @@ func NewListClinicsForClinicianRequest(server string, userId UserId, params *Lis return req, nil } -// NewEnableNewClinicExperienceRequest generates requests for EnableNewClinicExperience +// NewEnableNewClinicExperienceRequest constructs an http.Request for the EnableNewClinicExperience method func NewEnableNewClinicExperienceRequest(server string, userId string) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "userId", runtime.ParamLocationPath, userId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "userId", userId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -1903,7 +3217,7 @@ func NewEnableNewClinicExperienceRequest(server string, userId string) (*http.Re return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -1911,7 +3225,7 @@ func NewEnableNewClinicExperienceRequest(server string, userId string) (*http.Re return req, nil } -// NewListClinicsRequest generates requests for ListClinics +// NewListClinicsRequest constructs an http.Request for the ListClinics method func NewListClinicsRequest(server string, params *ListClinicsParams) (*http.Request, error) { var err error @@ -1931,19 +3245,21 @@ func NewListClinicsRequest(server string, params *ListClinicsParams) (*http.Requ } if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string if params.Limit != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -1951,15 +3267,11 @@ func NewListClinicsRequest(server string, params *ListClinicsParams) (*http.Requ if params.Offset != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "offset", runtime.ParamLocationQuery, *params.Offset); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", *params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -1967,15 +3279,11 @@ func NewListClinicsRequest(server string, params *ListClinicsParams) (*http.Requ if params.ShareCode != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "shareCode", runtime.ParamLocationQuery, *params.ShareCode); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "shareCode", *params.ShareCode, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -1983,15 +3291,11 @@ func NewListClinicsRequest(server string, params *ListClinicsParams) (*http.Requ if params.CreatedTimeStart != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "createdTimeStart", runtime.ParamLocationQuery, *params.CreatedTimeStart); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "createdTimeStart", *params.CreatedTimeStart, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -1999,15 +3303,11 @@ func NewListClinicsRequest(server string, params *ListClinicsParams) (*http.Requ if params.CreatedTimeEnd != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "createdTimeEnd", runtime.ParamLocationQuery, *params.CreatedTimeEnd); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "createdTimeEnd", *params.CreatedTimeEnd, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -2015,24 +3315,23 @@ func NewListClinicsRequest(server string, params *ListClinicsParams) (*http.Requ if params.EhrEnabled != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "ehrEnabled", runtime.ParamLocationQuery, *params.EhrEnabled); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "ehrEnabled", *params.EhrEnabled, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } } - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -2051,7 +3350,7 @@ func NewCreateClinicRequest(server string, body CreateClinicJSONRequestBody) (*h return NewCreateClinicRequestWithBody(server, "application/json", bodyReader) } -// NewCreateClinicRequestWithBody generates requests for CreateClinic with any type of body +// NewCreateClinicRequestWithBody constructs an http.Request for the CreateClinic method, with any body, and a specified content type func NewCreateClinicRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error @@ -2070,7 +3369,7 @@ func NewCreateClinicRequestWithBody(server string, contentType string, body io.R return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -2080,13 +3379,13 @@ func NewCreateClinicRequestWithBody(server string, contentType string, body io.R return req, nil } -// NewGetClinicByShareCodeRequest generates requests for GetClinicByShareCode +// NewGetClinicByShareCodeRequest constructs an http.Request for the GetClinicByShareCode method func NewGetClinicByShareCodeRequest(server string, shareCode string) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "shareCode", runtime.ParamLocationPath, shareCode) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "shareCode", shareCode, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2106,7 +3405,7 @@ func NewGetClinicByShareCodeRequest(server string, shareCode string) (*http.Requ return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -2114,13 +3413,13 @@ func NewGetClinicByShareCodeRequest(server string, shareCode string) (*http.Requ return req, nil } -// NewDeleteClinicRequest generates requests for DeleteClinic +// NewDeleteClinicRequest constructs an http.Request for the DeleteClinic method func NewDeleteClinicRequest(server string, clinicId ClinicId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2140,7 +3439,7 @@ func NewDeleteClinicRequest(server string, clinicId ClinicId) (*http.Request, er return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -2148,13 +3447,13 @@ func NewDeleteClinicRequest(server string, clinicId ClinicId) (*http.Request, er return req, nil } -// NewGetClinicRequest generates requests for GetClinic +// NewGetClinicRequest constructs an http.Request for the GetClinic method func NewGetClinicRequest(server string, clinicId ClinicId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2174,7 +3473,7 @@ func NewGetClinicRequest(server string, clinicId ClinicId) (*http.Request, error return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -2193,13 +3492,13 @@ func NewUpdateClinicRequest(server string, clinicId ClinicId, body UpdateClinicJ return NewUpdateClinicRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewUpdateClinicRequestWithBody generates requests for UpdateClinic with any type of body +// NewUpdateClinicRequestWithBody constructs an http.Request for the UpdateClinic method, with any body, and a specified content type func NewUpdateClinicRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2219,7 +3518,7 @@ func NewUpdateClinicRequestWithBody(server string, clinicId ClinicId, contentTyp return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -2229,13 +3528,13 @@ func NewUpdateClinicRequestWithBody(server string, clinicId ClinicId, contentTyp return req, nil } -// NewListCliniciansRequest generates requests for ListClinicians +// NewListCliniciansRequest constructs an http.Request for the ListClinicians method func NewListCliniciansRequest(server string, clinicId ClinicId, params *ListCliniciansParams) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2256,19 +3555,21 @@ func NewListCliniciansRequest(server string, clinicId ClinicId, params *ListClin } if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string if params.Search != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "search", runtime.ParamLocationQuery, *params.Search); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "search", *params.Search, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -2276,15 +3577,11 @@ func NewListCliniciansRequest(server string, clinicId ClinicId, params *ListClin if params.Offset != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "offset", runtime.ParamLocationQuery, *params.Offset); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", *params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -2292,15 +3589,11 @@ func NewListCliniciansRequest(server string, clinicId ClinicId, params *ListClin if params.Limit != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -2308,15 +3601,11 @@ func NewListCliniciansRequest(server string, clinicId ClinicId, params *ListClin if params.Email != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "email", runtime.ParamLocationQuery, *params.Email); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "email", *params.Email, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "email"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -2324,24 +3613,23 @@ func NewListCliniciansRequest(server string, clinicId ClinicId, params *ListClin if params.Role != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "role", runtime.ParamLocationQuery, *params.Role); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "role", *params.Role, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } } - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -2360,13 +3648,13 @@ func NewCreateClinicianRequest(server string, clinicId ClinicId, body CreateClin return NewCreateClinicianRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewCreateClinicianRequestWithBody generates requests for CreateClinician with any type of body +// NewCreateClinicianRequestWithBody constructs an http.Request for the CreateClinician method, with any body, and a specified content type func NewCreateClinicianRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2386,7 +3674,7 @@ func NewCreateClinicianRequestWithBody(server string, clinicId ClinicId, content return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -2396,20 +3684,20 @@ func NewCreateClinicianRequestWithBody(server string, clinicId ClinicId, content return req, nil } -// NewDeleteClinicianRequest generates requests for DeleteClinician +// NewDeleteClinicianRequest constructs an http.Request for the DeleteClinician method func NewDeleteClinicianRequest(server string, clinicId ClinicId, clinicianId ClinicianId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "clinicianId", runtime.ParamLocationPath, clinicianId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "clinicianId", clinicianId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2429,7 +3717,7 @@ func NewDeleteClinicianRequest(server string, clinicId ClinicId, clinicianId Cli return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -2437,20 +3725,20 @@ func NewDeleteClinicianRequest(server string, clinicId ClinicId, clinicianId Cli return req, nil } -// NewGetClinicianRequest generates requests for GetClinician +// NewGetClinicianRequest constructs an http.Request for the GetClinician method func NewGetClinicianRequest(server string, clinicId ClinicId, clinicianId ClinicianId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "clinicianId", runtime.ParamLocationPath, clinicianId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "clinicianId", clinicianId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2470,7 +3758,7 @@ func NewGetClinicianRequest(server string, clinicId ClinicId, clinicianId Clinic return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -2489,20 +3777,20 @@ func NewUpdateClinicianRequest(server string, clinicId ClinicId, clinicianId Cli return NewUpdateClinicianRequestWithBody(server, clinicId, clinicianId, "application/json", bodyReader) } -// NewUpdateClinicianRequestWithBody generates requests for UpdateClinician with any type of body +// NewUpdateClinicianRequestWithBody constructs an http.Request for the UpdateClinician method, with any body, and a specified content type func NewUpdateClinicianRequestWithBody(server string, clinicId ClinicId, clinicianId ClinicianId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "clinicianId", runtime.ParamLocationPath, clinicianId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "clinicianId", clinicianId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2522,7 +3810,7 @@ func NewUpdateClinicianRequestWithBody(server string, clinicId ClinicId, clinici return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -2532,13 +3820,13 @@ func NewUpdateClinicianRequestWithBody(server string, clinicId ClinicId, clinici return req, nil } -// NewSyncEHRDataRequest generates requests for SyncEHRData +// NewSyncEHRDataRequest constructs an http.Request for the SyncEHRData method func NewSyncEHRDataRequest(server string, clinicId ClinicId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2558,7 +3846,7 @@ func NewSyncEHRDataRequest(server string, clinicId ClinicId) (*http.Request, err return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -2566,20 +3854,20 @@ func NewSyncEHRDataRequest(server string, clinicId ClinicId) (*http.Request, err return req, nil } -// NewDeleteInvitedClinicianRequest generates requests for DeleteInvitedClinician +// NewDeleteInvitedClinicianRequest constructs an http.Request for the DeleteInvitedClinician method func NewDeleteInvitedClinicianRequest(server string, clinicId ClinicId, inviteId InviteId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "inviteId", runtime.ParamLocationPath, inviteId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "inviteId", inviteId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2599,7 +3887,7 @@ func NewDeleteInvitedClinicianRequest(server string, clinicId ClinicId, inviteId return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -2607,20 +3895,20 @@ func NewDeleteInvitedClinicianRequest(server string, clinicId ClinicId, inviteId return req, nil } -// NewGetInvitedClinicianRequest generates requests for GetInvitedClinician +// NewGetInvitedClinicianRequest constructs an http.Request for the GetInvitedClinician method func NewGetInvitedClinicianRequest(server string, clinicId ClinicId, inviteId InviteId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "inviteId", runtime.ParamLocationPath, inviteId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "inviteId", inviteId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2640,7 +3928,7 @@ func NewGetInvitedClinicianRequest(server string, clinicId ClinicId, inviteId In return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -2659,20 +3947,20 @@ func NewAssociateClinicianToUserRequest(server string, clinicId ClinicId, invite return NewAssociateClinicianToUserRequestWithBody(server, clinicId, inviteId, "application/json", bodyReader) } -// NewAssociateClinicianToUserRequestWithBody generates requests for AssociateClinicianToUser with any type of body +// NewAssociateClinicianToUserRequestWithBody constructs an http.Request for the AssociateClinicianToUser method, with any body, and a specified content type func NewAssociateClinicianToUserRequestWithBody(server string, clinicId ClinicId, inviteId InviteId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "inviteId", runtime.ParamLocationPath, inviteId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "inviteId", inviteId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2692,7 +3980,7 @@ func NewAssociateClinicianToUserRequestWithBody(server string, clinicId ClinicId return nil, err } - req, err := http.NewRequest("PATCH", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) if err != nil { return nil, err } @@ -2702,13 +3990,13 @@ func NewAssociateClinicianToUserRequestWithBody(server string, clinicId ClinicId return req, nil } -// NewListMembershipRestrictionsRequest generates requests for ListMembershipRestrictions +// NewListMembershipRestrictionsRequest constructs an http.Request for the ListMembershipRestrictions method func NewListMembershipRestrictionsRequest(server string, clinicId ClinicId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2728,7 +4016,7 @@ func NewListMembershipRestrictionsRequest(server string, clinicId ClinicId) (*ht return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -2747,13 +4035,13 @@ func NewUpdateMembershipRestrictionsRequest(server string, clinicId ClinicId, bo return NewUpdateMembershipRestrictionsRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewUpdateMembershipRestrictionsRequestWithBody generates requests for UpdateMembershipRestrictions with any type of body +// NewUpdateMembershipRestrictionsRequestWithBody constructs an http.Request for the UpdateMembershipRestrictions method, with any body, and a specified content type func NewUpdateMembershipRestrictionsRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2773,7 +4061,7 @@ func NewUpdateMembershipRestrictionsRequestWithBody(server string, clinicId Clin return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -2794,13 +4082,13 @@ func NewMergeClinicRequest(server string, clinicId ClinicId, body MergeClinicJSO return NewMergeClinicRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewMergeClinicRequestWithBody generates requests for MergeClinic with any type of body +// NewMergeClinicRequestWithBody constructs an http.Request for the MergeClinic method, with any body, and a specified content type func NewMergeClinicRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2820,7 +4108,7 @@ func NewMergeClinicRequestWithBody(server string, clinicId ClinicId, contentType return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -2841,13 +4129,13 @@ func NewTriggerInitialMigrationRequest(server string, clinicId string, body Trig return NewTriggerInitialMigrationRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewTriggerInitialMigrationRequestWithBody generates requests for TriggerInitialMigration with any type of body +// NewTriggerInitialMigrationRequestWithBody constructs an http.Request for the TriggerInitialMigration method, with any body, and a specified content type func NewTriggerInitialMigrationRequestWithBody(server string, clinicId string, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2867,7 +4155,7 @@ func NewTriggerInitialMigrationRequestWithBody(server string, clinicId string, c return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -2877,13 +4165,13 @@ func NewTriggerInitialMigrationRequestWithBody(server string, clinicId string, c return req, nil } -// NewListMigrationsRequest generates requests for ListMigrations +// NewListMigrationsRequest constructs an http.Request for the ListMigrations method func NewListMigrationsRequest(server string, clinicId string) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2903,7 +4191,7 @@ func NewListMigrationsRequest(server string, clinicId string) (*http.Request, er return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -2922,13 +4210,13 @@ func NewMigrateLegacyClinicianPatientsRequest(server string, clinicId string, bo return NewMigrateLegacyClinicianPatientsRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewMigrateLegacyClinicianPatientsRequestWithBody generates requests for MigrateLegacyClinicianPatients with any type of body +// NewMigrateLegacyClinicianPatientsRequestWithBody constructs an http.Request for the MigrateLegacyClinicianPatients method, with any body, and a specified content type func NewMigrateLegacyClinicianPatientsRequestWithBody(server string, clinicId string, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2948,7 +4236,7 @@ func NewMigrateLegacyClinicianPatientsRequestWithBody(server string, clinicId st return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -2958,20 +4246,20 @@ func NewMigrateLegacyClinicianPatientsRequestWithBody(server string, clinicId st return req, nil } -// NewGetMigrationRequest generates requests for GetMigration +// NewGetMigrationRequest constructs an http.Request for the GetMigration method func NewGetMigrationRequest(server string, clinicId ClinicIdV1, userId UserId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "userId", runtime.ParamLocationPath, userId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "userId", userId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -2991,7 +4279,7 @@ func NewGetMigrationRequest(server string, clinicId ClinicIdV1, userId UserId) ( return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -3010,20 +4298,20 @@ func NewUpdateMigrationRequest(server string, clinicId ClinicIdV1, userId UserId return NewUpdateMigrationRequestWithBody(server, clinicId, userId, "application/json", bodyReader) } -// NewUpdateMigrationRequestWithBody generates requests for UpdateMigration with any type of body +// NewUpdateMigrationRequestWithBody constructs an http.Request for the UpdateMigration method, with any body, and a specified content type func NewUpdateMigrationRequestWithBody(server string, clinicId ClinicIdV1, userId UserId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "userId", runtime.ParamLocationPath, userId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "userId", userId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -3043,7 +4331,7 @@ func NewUpdateMigrationRequestWithBody(server string, clinicId ClinicIdV1, userI return nil, err } - req, err := http.NewRequest("PATCH", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) if err != nil { return nil, err } @@ -3053,13 +4341,13 @@ func NewUpdateMigrationRequestWithBody(server string, clinicId ClinicIdV1, userI return req, nil } -// NewGetPatientCountRequest generates requests for GetPatientCount +// NewGetPatientCountRequest constructs an http.Request for the GetPatientCount method func NewGetPatientCountRequest(server string, clinicId ClinicId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -3079,7 +4367,7 @@ func NewGetPatientCountRequest(server string, clinicId ClinicId) (*http.Request, return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -3087,13 +4375,13 @@ func NewGetPatientCountRequest(server string, clinicId ClinicId) (*http.Request, return req, nil } -// NewRefreshPatientCountRequest generates requests for RefreshPatientCount +// NewRefreshPatientCountRequest constructs an http.Request for the RefreshPatientCount method func NewRefreshPatientCountRequest(server string, clinicId ClinicId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -3113,7 +4401,7 @@ func NewRefreshPatientCountRequest(server string, clinicId ClinicId) (*http.Requ return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -3132,13 +4420,13 @@ func NewCreatePatientTagRequest(server string, clinicId ClinicId, body CreatePat return NewCreatePatientTagRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewCreatePatientTagRequestWithBody generates requests for CreatePatientTag with any type of body +// NewCreatePatientTagRequestWithBody constructs an http.Request for the CreatePatientTag method, with any body, and a specified content type func NewCreatePatientTagRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -3158,7 +4446,7 @@ func NewCreatePatientTagRequestWithBody(server string, clinicId ClinicId, conten return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -3168,20 +4456,20 @@ func NewCreatePatientTagRequestWithBody(server string, clinicId ClinicId, conten return req, nil } -// NewDeletePatientTagRequest generates requests for DeletePatientTag +// NewDeletePatientTagRequest constructs an http.Request for the DeletePatientTag method func NewDeletePatientTagRequest(server string, clinicId ClinicId, patientTagId PatientTagId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientTagId", runtime.ParamLocationPath, patientTagId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientTagId", patientTagId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -3201,7 +4489,7 @@ func NewDeletePatientTagRequest(server string, clinicId ClinicId, patientTagId P return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -3220,20 +4508,20 @@ func NewUpdatePatientTagRequest(server string, clinicId ClinicId, patientTagId P return NewUpdatePatientTagRequestWithBody(server, clinicId, patientTagId, "application/json", bodyReader) } -// NewUpdatePatientTagRequestWithBody generates requests for UpdatePatientTag with any type of body +// NewUpdatePatientTagRequestWithBody constructs an http.Request for the UpdatePatientTag method, with any body, and a specified content type func NewUpdatePatientTagRequestWithBody(server string, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientTagId", runtime.ParamLocationPath, patientTagId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientTagId", patientTagId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -3253,7 +4541,7 @@ func NewUpdatePatientTagRequestWithBody(server string, clinicId ClinicId, patien return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -3263,20 +4551,20 @@ func NewUpdatePatientTagRequestWithBody(server string, clinicId ClinicId, patien return req, nil } -// NewConvertPatientTagToSiteRequest generates requests for ConvertPatientTagToSite +// NewConvertPatientTagToSiteRequest constructs an http.Request for the ConvertPatientTagToSite method func NewConvertPatientTagToSiteRequest(server string, clinicId ClinicId, patientTagId PatientTagId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientTagId", runtime.ParamLocationPath, patientTagId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientTagId", patientTagId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -3296,7 +4584,7 @@ func NewConvertPatientTagToSiteRequest(server string, clinicId ClinicId, patient return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -3304,13 +4592,13 @@ func NewConvertPatientTagToSiteRequest(server string, clinicId ClinicId, patient return req, nil } -// NewListPatientsRequest generates requests for ListPatients +// NewListPatientsRequest constructs an http.Request for the ListPatients method func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatientsParams) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -3331,19 +4619,21 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien } if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string if params.Search != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "search", runtime.ParamLocationQuery, *params.Search); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "search", *params.Search, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3351,15 +4641,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.Offset != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "offset", runtime.ParamLocationQuery, *params.Offset); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", *params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3367,15 +4653,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.Limit != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3383,15 +4665,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.Sort != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "sort", runtime.ParamLocationQuery, *params.Sort); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "sort", *params.Sort, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3399,15 +4677,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.SortType != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "sortType", runtime.ParamLocationQuery, *params.SortType); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "sortType", *params.SortType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3415,15 +4689,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.Period != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "period", runtime.ParamLocationQuery, *params.Period); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "period", *params.Period, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3431,15 +4701,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.LastReviewed != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "lastReviewed", runtime.ParamLocationQuery, *params.LastReviewed); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "lastReviewed", *params.LastReviewed, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3447,15 +4713,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmMax != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.max", runtime.ParamLocationQuery, *params.CgmMax); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.max", *params.CgmMax, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3463,15 +4725,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmMin != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.min", runtime.ParamLocationQuery, *params.CgmMin); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.min", *params.CgmMin, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3479,15 +4737,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmAverageGlucoseMmol != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.averageGlucoseMmol", runtime.ParamLocationQuery, *params.CgmAverageGlucoseMmol); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.averageGlucoseMmol", *params.CgmAverageGlucoseMmol, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3495,15 +4749,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmGlucoseManagementIndicator != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.glucoseManagementIndicator", runtime.ParamLocationQuery, *params.CgmGlucoseManagementIndicator); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.glucoseManagementIndicator", *params.CgmGlucoseManagementIndicator, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3511,15 +4761,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeCGMUsePercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeCGMUsePercent", runtime.ParamLocationQuery, *params.CgmTimeCGMUsePercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeCGMUsePercent", *params.CgmTimeCGMUsePercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3527,15 +4773,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryLowPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryLowPercent", runtime.ParamLocationQuery, *params.CgmTimeInVeryLowPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryLowPercent", *params.CgmTimeInVeryLowPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3543,15 +4785,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyLowPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyLowPercent", runtime.ParamLocationQuery, *params.CgmTimeInAnyLowPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyLowPercent", *params.CgmTimeInAnyLowPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3559,15 +4797,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInLowPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInLowPercent", runtime.ParamLocationQuery, *params.CgmTimeInLowPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInLowPercent", *params.CgmTimeInLowPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3575,15 +4809,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInTargetPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInTargetPercent", runtime.ParamLocationQuery, *params.CgmTimeInTargetPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInTargetPercent", *params.CgmTimeInTargetPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3591,15 +4821,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInHighPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInHighPercent", runtime.ParamLocationQuery, *params.CgmTimeInHighPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInHighPercent", *params.CgmTimeInHighPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3607,15 +4833,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryHighPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryHighPercent", runtime.ParamLocationQuery, *params.CgmTimeInVeryHighPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryHighPercent", *params.CgmTimeInVeryHighPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3623,15 +4845,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInExtremeHighPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInExtremeHighPercent", runtime.ParamLocationQuery, *params.CgmTimeInExtremeHighPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInExtremeHighPercent", *params.CgmTimeInExtremeHighPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3639,15 +4857,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyHighPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyHighPercent", runtime.ParamLocationQuery, *params.CgmTimeInAnyHighPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyHighPercent", *params.CgmTimeInAnyHighPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3655,15 +4869,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeCGMUseMinutes != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeCGMUseMinutes", runtime.ParamLocationQuery, *params.CgmTimeCGMUseMinutes); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeCGMUseMinutes", *params.CgmTimeCGMUseMinutes, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3671,15 +4881,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryLowMinutes != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryLowMinutes", runtime.ParamLocationQuery, *params.CgmTimeInVeryLowMinutes); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryLowMinutes", *params.CgmTimeInVeryLowMinutes, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3687,15 +4893,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyLowMinutes != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyLowMinutes", runtime.ParamLocationQuery, *params.CgmTimeInAnyLowMinutes); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyLowMinutes", *params.CgmTimeInAnyLowMinutes, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3703,15 +4905,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInLowMinutes != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInLowMinutes", runtime.ParamLocationQuery, *params.CgmTimeInLowMinutes); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInLowMinutes", *params.CgmTimeInLowMinutes, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3719,15 +4917,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInTargetMinutes != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInTargetMinutes", runtime.ParamLocationQuery, *params.CgmTimeInTargetMinutes); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInTargetMinutes", *params.CgmTimeInTargetMinutes, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3735,15 +4929,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInHighMinutes != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInHighMinutes", runtime.ParamLocationQuery, *params.CgmTimeInHighMinutes); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInHighMinutes", *params.CgmTimeInHighMinutes, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3751,15 +4941,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryHighMinutes != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryHighMinutes", runtime.ParamLocationQuery, *params.CgmTimeInVeryHighMinutes); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryHighMinutes", *params.CgmTimeInVeryHighMinutes, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3767,15 +4953,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInExtremeHighMinutes != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInExtremeHighMinutes", runtime.ParamLocationQuery, *params.CgmTimeInExtremeHighMinutes); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInExtremeHighMinutes", *params.CgmTimeInExtremeHighMinutes, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3783,15 +4965,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyHighMinutes != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyHighMinutes", runtime.ParamLocationQuery, *params.CgmTimeInAnyHighMinutes); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyHighMinutes", *params.CgmTimeInAnyHighMinutes, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3799,15 +4977,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeCGMUseRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeCGMUseRecords", runtime.ParamLocationQuery, *params.CgmTimeCGMUseRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeCGMUseRecords", *params.CgmTimeCGMUseRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3815,15 +4989,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryLowRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryLowRecords", runtime.ParamLocationQuery, *params.CgmTimeInVeryLowRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryLowRecords", *params.CgmTimeInVeryLowRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3831,15 +5001,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyLowRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyLowRecords", runtime.ParamLocationQuery, *params.CgmTimeInAnyLowRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyLowRecords", *params.CgmTimeInAnyLowRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3847,15 +5013,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInLowRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInLowRecords", runtime.ParamLocationQuery, *params.CgmTimeInLowRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInLowRecords", *params.CgmTimeInLowRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3863,15 +5025,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInTargetRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInTargetRecords", runtime.ParamLocationQuery, *params.CgmTimeInTargetRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInTargetRecords", *params.CgmTimeInTargetRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3879,15 +5037,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInHighRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInHighRecords", runtime.ParamLocationQuery, *params.CgmTimeInHighRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInHighRecords", *params.CgmTimeInHighRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3895,15 +5049,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryHighRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryHighRecords", runtime.ParamLocationQuery, *params.CgmTimeInVeryHighRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryHighRecords", *params.CgmTimeInVeryHighRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3911,15 +5061,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyHighRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyHighRecords", runtime.ParamLocationQuery, *params.CgmTimeInAnyHighRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyHighRecords", *params.CgmTimeInAnyHighRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3927,15 +5073,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmAverageDailyRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.averageDailyRecords", runtime.ParamLocationQuery, *params.CgmAverageDailyRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.averageDailyRecords", *params.CgmAverageDailyRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3943,15 +5085,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTotalRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.totalRecords", runtime.ParamLocationQuery, *params.CgmTotalRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.totalRecords", *params.CgmTotalRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3959,15 +5097,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmHoursWithData != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.hoursWithData", runtime.ParamLocationQuery, *params.CgmHoursWithData); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.hoursWithData", *params.CgmHoursWithData, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3975,15 +5109,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmDaysWithData != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.daysWithData", runtime.ParamLocationQuery, *params.CgmDaysWithData); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.daysWithData", *params.CgmDaysWithData, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -3991,15 +5121,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmStandardDeviation != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.standardDeviation", runtime.ParamLocationQuery, *params.CgmStandardDeviation); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.standardDeviation", *params.CgmStandardDeviation, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4007,15 +5133,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmCoefficientOfVariation != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.coefficientOfVariation", runtime.ParamLocationQuery, *params.CgmCoefficientOfVariation); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.coefficientOfVariation", *params.CgmCoefficientOfVariation, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4023,15 +5145,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmMax != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.max", runtime.ParamLocationQuery, *params.BgmMax); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.max", *params.BgmMax, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4039,15 +5157,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmMin != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.min", runtime.ParamLocationQuery, *params.BgmMin); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.min", *params.BgmMin, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4055,15 +5169,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmAverageGlucoseMmol != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.averageGlucoseMmol", runtime.ParamLocationQuery, *params.BgmAverageGlucoseMmol); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.averageGlucoseMmol", *params.BgmAverageGlucoseMmol, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4071,15 +5181,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInVeryLowPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInVeryLowPercent", runtime.ParamLocationQuery, *params.BgmTimeInVeryLowPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInVeryLowPercent", *params.BgmTimeInVeryLowPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4087,15 +5193,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInAnyLowPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInAnyLowPercent", runtime.ParamLocationQuery, *params.BgmTimeInAnyLowPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInAnyLowPercent", *params.BgmTimeInAnyLowPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4103,15 +5205,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInLowPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInLowPercent", runtime.ParamLocationQuery, *params.BgmTimeInLowPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInLowPercent", *params.BgmTimeInLowPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4119,15 +5217,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInTargetPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInTargetPercent", runtime.ParamLocationQuery, *params.BgmTimeInTargetPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInTargetPercent", *params.BgmTimeInTargetPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4135,15 +5229,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInHighPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInHighPercent", runtime.ParamLocationQuery, *params.BgmTimeInHighPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInHighPercent", *params.BgmTimeInHighPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4151,15 +5241,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInVeryHighPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInVeryHighPercent", runtime.ParamLocationQuery, *params.BgmTimeInVeryHighPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInVeryHighPercent", *params.BgmTimeInVeryHighPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4167,15 +5253,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInExtremeHighPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInExtremeHighPercent", runtime.ParamLocationQuery, *params.BgmTimeInExtremeHighPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInExtremeHighPercent", *params.BgmTimeInExtremeHighPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4183,15 +5265,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInAnyHighPercent != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInAnyHighPercent", runtime.ParamLocationQuery, *params.BgmTimeInAnyHighPercent); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInAnyHighPercent", *params.BgmTimeInAnyHighPercent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4199,15 +5277,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInVeryLowRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInVeryLowRecords", runtime.ParamLocationQuery, *params.BgmTimeInVeryLowRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInVeryLowRecords", *params.BgmTimeInVeryLowRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4215,15 +5289,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInAnyLowRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInAnyLowRecords", runtime.ParamLocationQuery, *params.BgmTimeInAnyLowRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInAnyLowRecords", *params.BgmTimeInAnyLowRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4231,15 +5301,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInLowRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInLowRecords", runtime.ParamLocationQuery, *params.BgmTimeInLowRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInLowRecords", *params.BgmTimeInLowRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4247,15 +5313,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInTargetRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInTargetRecords", runtime.ParamLocationQuery, *params.BgmTimeInTargetRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInTargetRecords", *params.BgmTimeInTargetRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4263,15 +5325,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInHighRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInHighRecords", runtime.ParamLocationQuery, *params.BgmTimeInHighRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInHighRecords", *params.BgmTimeInHighRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4279,15 +5337,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInVeryHighRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInVeryHighRecords", runtime.ParamLocationQuery, *params.BgmTimeInVeryHighRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInVeryHighRecords", *params.BgmTimeInVeryHighRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4295,15 +5349,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInAnyHighRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInAnyHighRecords", runtime.ParamLocationQuery, *params.BgmTimeInAnyHighRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInAnyHighRecords", *params.BgmTimeInAnyHighRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4311,15 +5361,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmAverageDailyRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.averageDailyRecords", runtime.ParamLocationQuery, *params.BgmAverageDailyRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.averageDailyRecords", *params.BgmAverageDailyRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4327,15 +5373,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTotalRecords != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.totalRecords", runtime.ParamLocationQuery, *params.BgmTotalRecords); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.totalRecords", *params.BgmTotalRecords, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4343,15 +5385,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmMaxDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.maxDelta", runtime.ParamLocationQuery, *params.CgmMaxDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.maxDelta", *params.CgmMaxDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4359,15 +5397,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmMinDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.minDelta", runtime.ParamLocationQuery, *params.CgmMinDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.minDelta", *params.CgmMinDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4375,15 +5409,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmAverageGlucoseMmolDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.averageGlucoseMmolDelta", runtime.ParamLocationQuery, *params.CgmAverageGlucoseMmolDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.averageGlucoseMmolDelta", *params.CgmAverageGlucoseMmolDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4391,15 +5421,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmGlucoseManagementIndicatorDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.glucoseManagementIndicatorDelta", runtime.ParamLocationQuery, *params.CgmGlucoseManagementIndicatorDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.glucoseManagementIndicatorDelta", *params.CgmGlucoseManagementIndicatorDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4407,15 +5433,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeCGMUsePercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeCGMUsePercentDelta", runtime.ParamLocationQuery, *params.CgmTimeCGMUsePercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeCGMUsePercentDelta", *params.CgmTimeCGMUsePercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4423,15 +5445,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryLowPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryLowPercentDelta", runtime.ParamLocationQuery, *params.CgmTimeInVeryLowPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryLowPercentDelta", *params.CgmTimeInVeryLowPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4439,15 +5457,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyLowPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyLowPercentDelta", runtime.ParamLocationQuery, *params.CgmTimeInAnyLowPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyLowPercentDelta", *params.CgmTimeInAnyLowPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4455,15 +5469,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInLowPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInLowPercentDelta", runtime.ParamLocationQuery, *params.CgmTimeInLowPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInLowPercentDelta", *params.CgmTimeInLowPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4471,15 +5481,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInTargetPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInTargetPercentDelta", runtime.ParamLocationQuery, *params.CgmTimeInTargetPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInTargetPercentDelta", *params.CgmTimeInTargetPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4487,15 +5493,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInHighPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInHighPercentDelta", runtime.ParamLocationQuery, *params.CgmTimeInHighPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInHighPercentDelta", *params.CgmTimeInHighPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4503,15 +5505,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryHighPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryHighPercentDelta", runtime.ParamLocationQuery, *params.CgmTimeInVeryHighPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryHighPercentDelta", *params.CgmTimeInVeryHighPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4519,15 +5517,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInExtremeHighPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInExtremeHighPercentDelta", runtime.ParamLocationQuery, *params.CgmTimeInExtremeHighPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInExtremeHighPercentDelta", *params.CgmTimeInExtremeHighPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4535,15 +5529,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyHighPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyHighPercentDelta", runtime.ParamLocationQuery, *params.CgmTimeInAnyHighPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyHighPercentDelta", *params.CgmTimeInAnyHighPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4551,15 +5541,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeCGMUseMinutesDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeCGMUseMinutesDelta", runtime.ParamLocationQuery, *params.CgmTimeCGMUseMinutesDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeCGMUseMinutesDelta", *params.CgmTimeCGMUseMinutesDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4567,15 +5553,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryLowMinutesDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryLowMinutesDelta", runtime.ParamLocationQuery, *params.CgmTimeInVeryLowMinutesDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryLowMinutesDelta", *params.CgmTimeInVeryLowMinutesDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4583,15 +5565,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyLowMinutesDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyLowMinutesDelta", runtime.ParamLocationQuery, *params.CgmTimeInAnyLowMinutesDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyLowMinutesDelta", *params.CgmTimeInAnyLowMinutesDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4599,15 +5577,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInLowMinutesDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInLowMinutesDelta", runtime.ParamLocationQuery, *params.CgmTimeInLowMinutesDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInLowMinutesDelta", *params.CgmTimeInLowMinutesDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4615,15 +5589,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInTargetMinutesDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInTargetMinutesDelta", runtime.ParamLocationQuery, *params.CgmTimeInTargetMinutesDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInTargetMinutesDelta", *params.CgmTimeInTargetMinutesDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4631,15 +5601,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInHighMinutesDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInHighMinutesDelta", runtime.ParamLocationQuery, *params.CgmTimeInHighMinutesDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInHighMinutesDelta", *params.CgmTimeInHighMinutesDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4647,15 +5613,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryHighMinutesDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryHighMinutesDelta", runtime.ParamLocationQuery, *params.CgmTimeInVeryHighMinutesDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryHighMinutesDelta", *params.CgmTimeInVeryHighMinutesDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4663,15 +5625,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInExtremeHighMinutesDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInExtremeHighMinutesDelta", runtime.ParamLocationQuery, *params.CgmTimeInExtremeHighMinutesDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInExtremeHighMinutesDelta", *params.CgmTimeInExtremeHighMinutesDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4679,15 +5637,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyHighMinutesDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyHighMinutesDelta", runtime.ParamLocationQuery, *params.CgmTimeInAnyHighMinutesDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyHighMinutesDelta", *params.CgmTimeInAnyHighMinutesDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4695,15 +5649,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeCGMUseRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeCGMUseRecordsDelta", runtime.ParamLocationQuery, *params.CgmTimeCGMUseRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeCGMUseRecordsDelta", *params.CgmTimeCGMUseRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4711,15 +5661,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryLowRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryLowRecordsDelta", runtime.ParamLocationQuery, *params.CgmTimeInVeryLowRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryLowRecordsDelta", *params.CgmTimeInVeryLowRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4727,15 +5673,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyLowRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyLowRecordsDelta", runtime.ParamLocationQuery, *params.CgmTimeInAnyLowRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyLowRecordsDelta", *params.CgmTimeInAnyLowRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4743,15 +5685,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInLowRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInLowRecordsDelta", runtime.ParamLocationQuery, *params.CgmTimeInLowRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInLowRecordsDelta", *params.CgmTimeInLowRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4759,15 +5697,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInTargetRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInTargetRecordsDelta", runtime.ParamLocationQuery, *params.CgmTimeInTargetRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInTargetRecordsDelta", *params.CgmTimeInTargetRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4775,15 +5709,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInHighRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInHighRecordsDelta", runtime.ParamLocationQuery, *params.CgmTimeInHighRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInHighRecordsDelta", *params.CgmTimeInHighRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4791,15 +5721,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInVeryHighRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInVeryHighRecordsDelta", runtime.ParamLocationQuery, *params.CgmTimeInVeryHighRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInVeryHighRecordsDelta", *params.CgmTimeInVeryHighRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4807,15 +5733,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTimeInAnyHighRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.timeInAnyHighRecordsDelta", runtime.ParamLocationQuery, *params.CgmTimeInAnyHighRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.timeInAnyHighRecordsDelta", *params.CgmTimeInAnyHighRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4823,15 +5745,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmAverageDailyRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.averageDailyRecordsDelta", runtime.ParamLocationQuery, *params.CgmAverageDailyRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.averageDailyRecordsDelta", *params.CgmAverageDailyRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4839,15 +5757,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmTotalRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.totalRecordsDelta", runtime.ParamLocationQuery, *params.CgmTotalRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.totalRecordsDelta", *params.CgmTotalRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4855,15 +5769,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmHoursWithDataDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.hoursWithDataDelta", runtime.ParamLocationQuery, *params.CgmHoursWithDataDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.hoursWithDataDelta", *params.CgmHoursWithDataDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4871,15 +5781,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmDaysWithDataDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.daysWithDataDelta", runtime.ParamLocationQuery, *params.CgmDaysWithDataDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.daysWithDataDelta", *params.CgmDaysWithDataDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4887,15 +5793,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmStandardDeviationDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.standardDeviationDelta", runtime.ParamLocationQuery, *params.CgmStandardDeviationDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.standardDeviationDelta", *params.CgmStandardDeviationDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4903,15 +5805,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmCoefficientOfVariationDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.coefficientOfVariationDelta", runtime.ParamLocationQuery, *params.CgmCoefficientOfVariationDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.coefficientOfVariationDelta", *params.CgmCoefficientOfVariationDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4919,15 +5817,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmLastDataFrom != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.lastDataFrom", runtime.ParamLocationQuery, *params.CgmLastDataFrom); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.lastDataFrom", *params.CgmLastDataFrom, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4935,15 +5829,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.CgmLastDataTo != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "cgm.lastDataTo", runtime.ParamLocationQuery, *params.CgmLastDataTo); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cgm.lastDataTo", *params.CgmLastDataTo, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4951,15 +5841,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmMaxDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.maxDelta", runtime.ParamLocationQuery, *params.BgmMaxDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.maxDelta", *params.BgmMaxDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4967,15 +5853,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmMinDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.minDelta", runtime.ParamLocationQuery, *params.BgmMinDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.minDelta", *params.BgmMinDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4983,15 +5865,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmAverageGlucoseMmolDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.averageGlucoseMmolDelta", runtime.ParamLocationQuery, *params.BgmAverageGlucoseMmolDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.averageGlucoseMmolDelta", *params.BgmAverageGlucoseMmolDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -4999,15 +5877,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInVeryLowPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInVeryLowPercentDelta", runtime.ParamLocationQuery, *params.BgmTimeInVeryLowPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInVeryLowPercentDelta", *params.BgmTimeInVeryLowPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5015,15 +5889,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInAnyLowPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInAnyLowPercentDelta", runtime.ParamLocationQuery, *params.BgmTimeInAnyLowPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInAnyLowPercentDelta", *params.BgmTimeInAnyLowPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5031,15 +5901,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInLowPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInLowPercentDelta", runtime.ParamLocationQuery, *params.BgmTimeInLowPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInLowPercentDelta", *params.BgmTimeInLowPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5047,15 +5913,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInTargetPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInTargetPercentDelta", runtime.ParamLocationQuery, *params.BgmTimeInTargetPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInTargetPercentDelta", *params.BgmTimeInTargetPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5063,15 +5925,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInHighPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInHighPercentDelta", runtime.ParamLocationQuery, *params.BgmTimeInHighPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInHighPercentDelta", *params.BgmTimeInHighPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5079,15 +5937,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInVeryHighPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInVeryHighPercentDelta", runtime.ParamLocationQuery, *params.BgmTimeInVeryHighPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInVeryHighPercentDelta", *params.BgmTimeInVeryHighPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5095,15 +5949,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInExtremeHighPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInExtremeHighPercentDelta", runtime.ParamLocationQuery, *params.BgmTimeInExtremeHighPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInExtremeHighPercentDelta", *params.BgmTimeInExtremeHighPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5111,15 +5961,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInAnyHighPercentDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInAnyHighPercentDelta", runtime.ParamLocationQuery, *params.BgmTimeInAnyHighPercentDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInAnyHighPercentDelta", *params.BgmTimeInAnyHighPercentDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5127,15 +5973,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInVeryLowRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInVeryLowRecordsDelta", runtime.ParamLocationQuery, *params.BgmTimeInVeryLowRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInVeryLowRecordsDelta", *params.BgmTimeInVeryLowRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5143,15 +5985,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInAnyLowRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInAnyLowRecordsDelta", runtime.ParamLocationQuery, *params.BgmTimeInAnyLowRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInAnyLowRecordsDelta", *params.BgmTimeInAnyLowRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5159,15 +5997,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInLowRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInLowRecordsDelta", runtime.ParamLocationQuery, *params.BgmTimeInLowRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInLowRecordsDelta", *params.BgmTimeInLowRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5175,15 +6009,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInTargetRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInTargetRecordsDelta", runtime.ParamLocationQuery, *params.BgmTimeInTargetRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInTargetRecordsDelta", *params.BgmTimeInTargetRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5191,15 +6021,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInHighRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInHighRecordsDelta", runtime.ParamLocationQuery, *params.BgmTimeInHighRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInHighRecordsDelta", *params.BgmTimeInHighRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5207,15 +6033,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInVeryHighRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInVeryHighRecordsDelta", runtime.ParamLocationQuery, *params.BgmTimeInVeryHighRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInVeryHighRecordsDelta", *params.BgmTimeInVeryHighRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5223,15 +6045,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTimeInAnyHighRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.timeInAnyHighRecordsDelta", runtime.ParamLocationQuery, *params.BgmTimeInAnyHighRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.timeInAnyHighRecordsDelta", *params.BgmTimeInAnyHighRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5239,15 +6057,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmAverageDailyRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.averageDailyRecordsDelta", runtime.ParamLocationQuery, *params.BgmAverageDailyRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.averageDailyRecordsDelta", *params.BgmAverageDailyRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5255,15 +6069,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmTotalRecordsDelta != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.totalRecordsDelta", runtime.ParamLocationQuery, *params.BgmTotalRecordsDelta); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.totalRecordsDelta", *params.BgmTotalRecordsDelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5271,15 +6081,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmLastDataFrom != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.lastDataFrom", runtime.ParamLocationQuery, *params.BgmLastDataFrom); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.lastDataFrom", *params.BgmLastDataFrom, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5287,15 +6093,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.BgmLastDataTo != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "bgm.lastDataTo", runtime.ParamLocationQuery, *params.BgmLastDataTo); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bgm.lastDataTo", *params.BgmLastDataTo, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5303,15 +6105,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.Tags != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", false, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", false, "tags", *params.Tags, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5319,15 +6117,11 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.Sites != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", false, "sites", runtime.ParamLocationQuery, *params.Sites); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", false, "sites", *params.Sites, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -5335,24 +6129,23 @@ func NewListPatientsRequest(server string, clinicId ClinicId, params *ListPatien if params.OmitNonStandardRanges != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "omitNonStandardRanges", runtime.ParamLocationQuery, *params.OmitNonStandardRanges); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "omitNonStandardRanges", *params.OmitNonStandardRanges, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } } - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -5371,13 +6164,13 @@ func NewCreatePatientAccountRequest(server string, clinicId ClinicId, body Creat return NewCreatePatientAccountRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewCreatePatientAccountRequestWithBody generates requests for CreatePatientAccount with any type of body +// NewCreatePatientAccountRequestWithBody constructs an http.Request for the CreatePatientAccount method, with any body, and a specified content type func NewCreatePatientAccountRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5397,7 +6190,7 @@ func NewCreatePatientAccountRequestWithBody(server string, clinicId ClinicId, co return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -5418,20 +6211,20 @@ func NewAssignPatientTagToClinicPatientsRequest(server string, clinicId ClinicId return NewAssignPatientTagToClinicPatientsRequestWithBody(server, clinicId, patientTagId, "application/json", bodyReader) } -// NewAssignPatientTagToClinicPatientsRequestWithBody generates requests for AssignPatientTagToClinicPatients with any type of body +// NewAssignPatientTagToClinicPatientsRequestWithBody constructs an http.Request for the AssignPatientTagToClinicPatients method, with any body, and a specified content type func NewAssignPatientTagToClinicPatientsRequestWithBody(server string, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientTagId", runtime.ParamLocationPath, patientTagId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientTagId", patientTagId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5451,7 +6244,7 @@ func NewAssignPatientTagToClinicPatientsRequestWithBody(server string, clinicId return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -5472,20 +6265,20 @@ func NewDeletePatientTagFromClinicPatientsRequest(server string, clinicId Clinic return NewDeletePatientTagFromClinicPatientsRequestWithBody(server, clinicId, patientTagId, "application/json", bodyReader) } -// NewDeletePatientTagFromClinicPatientsRequestWithBody generates requests for DeletePatientTagFromClinicPatients with any type of body +// NewDeletePatientTagFromClinicPatientsRequestWithBody constructs an http.Request for the DeletePatientTagFromClinicPatients method, with any body, and a specified content type func NewDeletePatientTagFromClinicPatientsRequestWithBody(server string, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientTagId", runtime.ParamLocationPath, patientTagId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientTagId", patientTagId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5505,7 +6298,7 @@ func NewDeletePatientTagFromClinicPatientsRequestWithBody(server string, clinicI return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -5515,20 +6308,20 @@ func NewDeletePatientTagFromClinicPatientsRequestWithBody(server string, clinicI return req, nil } -// NewDeletePatientRequest generates requests for DeletePatient +// NewDeletePatientRequest constructs an http.Request for the DeletePatient method func NewDeletePatientRequest(server string, clinicId ClinicId, patientId PatientId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5548,7 +6341,7 @@ func NewDeletePatientRequest(server string, clinicId ClinicId, patientId Patient return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -5556,20 +6349,20 @@ func NewDeletePatientRequest(server string, clinicId ClinicId, patientId Patient return req, nil } -// NewGetPatientRequest generates requests for GetPatient +// NewGetPatientRequest constructs an http.Request for the GetPatient method func NewGetPatientRequest(server string, clinicId ClinicId, patientId PatientId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5589,7 +6382,7 @@ func NewGetPatientRequest(server string, clinicId ClinicId, patientId PatientId) return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -5608,20 +6401,20 @@ func NewCreatePatientFromUserRequest(server string, clinicId ClinicId, patientId return NewCreatePatientFromUserRequestWithBody(server, clinicId, patientId, "application/json", bodyReader) } -// NewCreatePatientFromUserRequestWithBody generates requests for CreatePatientFromUser with any type of body +// NewCreatePatientFromUserRequestWithBody constructs an http.Request for the CreatePatientFromUser method, with any body, and a specified content type func NewCreatePatientFromUserRequestWithBody(server string, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5641,7 +6434,7 @@ func NewCreatePatientFromUserRequestWithBody(server string, clinicId ClinicId, p return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -5662,20 +6455,20 @@ func NewUpdatePatientRequest(server string, clinicId ClinicId, patientId Patient return NewUpdatePatientRequestWithBody(server, clinicId, patientId, "application/json", bodyReader) } -// NewUpdatePatientRequestWithBody generates requests for UpdatePatient with any type of body +// NewUpdatePatientRequestWithBody constructs an http.Request for the UpdatePatient method, with any body, and a specified content type func NewUpdatePatientRequestWithBody(server string, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5695,7 +6488,7 @@ func NewUpdatePatientRequestWithBody(server string, clinicId ClinicId, patientId return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -5705,27 +6498,27 @@ func NewUpdatePatientRequestWithBody(server string, clinicId ClinicId, patientId return req, nil } -// NewConnectProviderRequest generates requests for ConnectProvider +// NewConnectProviderRequest constructs an http.Request for the ConnectProvider method func NewConnectProviderRequest(server string, clinicId ClinicId, patientId PatientId, providerId ProviderId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam2 string - pathParam2, err = runtime.StyleParamWithLocation("simple", false, "providerId", runtime.ParamLocationPath, providerId) + pathParam2, err = runtime.StyleParamWithOptions("simple", false, "providerId", providerId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5745,7 +6538,7 @@ func NewConnectProviderRequest(server string, clinicId ClinicId, patientId Patie return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -5764,20 +6557,20 @@ func NewUpdatePatientPermissionsRequest(server string, clinicId ClinicId, patien return NewUpdatePatientPermissionsRequestWithBody(server, clinicId, patientId, "application/json", bodyReader) } -// NewUpdatePatientPermissionsRequestWithBody generates requests for UpdatePatientPermissions with any type of body +// NewUpdatePatientPermissionsRequestWithBody constructs an http.Request for the UpdatePatientPermissions method, with any body, and a specified content type func NewUpdatePatientPermissionsRequestWithBody(server string, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5797,7 +6590,7 @@ func NewUpdatePatientPermissionsRequestWithBody(server string, clinicId ClinicId return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -5807,27 +6600,27 @@ func NewUpdatePatientPermissionsRequestWithBody(server string, clinicId ClinicId return req, nil } -// NewDeletePatientPermissionRequest generates requests for DeletePatientPermission +// NewDeletePatientPermissionRequest constructs an http.Request for the DeletePatientPermission method func NewDeletePatientPermissionRequest(server string, clinicId ClinicId, patientId PatientId, permission string) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam2 string - pathParam2, err = runtime.StyleParamWithLocation("simple", false, "permission", runtime.ParamLocationPath, permission) + pathParam2, err = runtime.StyleParamWithOptions("simple", false, "permission", permission, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5847,7 +6640,7 @@ func NewDeletePatientPermissionRequest(server string, clinicId ClinicId, patient return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -5855,20 +6648,20 @@ func NewDeletePatientPermissionRequest(server string, clinicId ClinicId, patient return req, nil } -// NewDeletePatientReviewsRequest generates requests for DeletePatientReviews +// NewDeletePatientReviewsRequest constructs an http.Request for the DeletePatientReviews method func NewDeletePatientReviewsRequest(server string, clinicId ClinicId, patientId PatientId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5888,7 +6681,7 @@ func NewDeletePatientReviewsRequest(server string, clinicId ClinicId, patientId return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -5896,20 +6689,20 @@ func NewDeletePatientReviewsRequest(server string, clinicId ClinicId, patientId return req, nil } -// NewUpdatePatientReviewsRequest generates requests for UpdatePatientReviews +// NewUpdatePatientReviewsRequest constructs an http.Request for the UpdatePatientReviews method func NewUpdatePatientReviewsRequest(server string, clinicId ClinicId, patientId PatientId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5929,7 +6722,7 @@ func NewUpdatePatientReviewsRequest(server string, clinicId ClinicId, patientId return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), nil) if err != nil { return nil, err } @@ -5937,20 +6730,20 @@ func NewUpdatePatientReviewsRequest(server string, clinicId ClinicId, patientId return req, nil } -// NewSendUploadReminderRequest generates requests for SendUploadReminder +// NewSendUploadReminderRequest constructs an http.Request for the SendUploadReminder method func NewSendUploadReminderRequest(server string, clinicId ClinicId, patientId PatientId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -5970,7 +6763,7 @@ func NewSendUploadReminderRequest(server string, clinicId ClinicId, patientId Pa return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -5989,13 +6782,13 @@ func NewGenerateMergeReportRequest(server string, clinicId ClinicId, body Genera return NewGenerateMergeReportRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewGenerateMergeReportRequestWithBody generates requests for GenerateMergeReport with any type of body +// NewGenerateMergeReportRequestWithBody constructs an http.Request for the GenerateMergeReport method, with any body, and a specified content type func NewGenerateMergeReportRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6015,7 +6808,7 @@ func NewGenerateMergeReportRequestWithBody(server string, clinicId ClinicId, con return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -6036,13 +6829,13 @@ func NewAddServiceAccountRequest(server string, clinicId ClinicId, body AddServi return NewAddServiceAccountRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewAddServiceAccountRequestWithBody generates requests for AddServiceAccount with any type of body +// NewAddServiceAccountRequestWithBody constructs an http.Request for the AddServiceAccount method, with any body, and a specified content type func NewAddServiceAccountRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6062,7 +6855,7 @@ func NewAddServiceAccountRequestWithBody(server string, clinicId ClinicId, conte return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -6072,13 +6865,13 @@ func NewAddServiceAccountRequestWithBody(server string, clinicId ClinicId, conte return req, nil } -// NewGetEHRSettingsRequest generates requests for GetEHRSettings +// NewGetEHRSettingsRequest constructs an http.Request for the GetEHRSettings method func NewGetEHRSettingsRequest(server string, clinicId ClinicId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6098,7 +6891,7 @@ func NewGetEHRSettingsRequest(server string, clinicId ClinicId) (*http.Request, return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -6117,13 +6910,13 @@ func NewUpdateEHRSettingsRequest(server string, clinicId ClinicId, body UpdateEH return NewUpdateEHRSettingsRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewUpdateEHRSettingsRequestWithBody generates requests for UpdateEHRSettings with any type of body +// NewUpdateEHRSettingsRequestWithBody constructs an http.Request for the UpdateEHRSettings method, with any body, and a specified content type func NewUpdateEHRSettingsRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6143,7 +6936,7 @@ func NewUpdateEHRSettingsRequestWithBody(server string, clinicId ClinicId, conte return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -6153,13 +6946,13 @@ func NewUpdateEHRSettingsRequestWithBody(server string, clinicId ClinicId, conte return req, nil } -// NewGetMRNSettingsRequest generates requests for GetMRNSettings +// NewGetMRNSettingsRequest constructs an http.Request for the GetMRNSettings method func NewGetMRNSettingsRequest(server string, clinicId ClinicId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6179,7 +6972,7 @@ func NewGetMRNSettingsRequest(server string, clinicId ClinicId) (*http.Request, return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -6198,13 +6991,13 @@ func NewUpdateMRNSettingsRequest(server string, clinicId ClinicId, body UpdateMR return NewUpdateMRNSettingsRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewUpdateMRNSettingsRequestWithBody generates requests for UpdateMRNSettings with any type of body +// NewUpdateMRNSettingsRequestWithBody constructs an http.Request for the UpdateMRNSettings method, with any body, and a specified content type func NewUpdateMRNSettingsRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6224,7 +7017,7 @@ func NewUpdateMRNSettingsRequestWithBody(server string, clinicId ClinicId, conte return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -6234,13 +7027,13 @@ func NewUpdateMRNSettingsRequestWithBody(server string, clinicId ClinicId, conte return req, nil } -// NewGetPatientCountSettingsRequest generates requests for GetPatientCountSettings +// NewGetPatientCountSettingsRequest constructs an http.Request for the GetPatientCountSettings method func NewGetPatientCountSettingsRequest(server string, clinicId ClinicId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6260,7 +7053,7 @@ func NewGetPatientCountSettingsRequest(server string, clinicId ClinicId) (*http. return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -6279,13 +7072,13 @@ func NewUpdatePatientCountSettingsRequest(server string, clinicId ClinicId, body return NewUpdatePatientCountSettingsRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewUpdatePatientCountSettingsRequestWithBody generates requests for UpdatePatientCountSettings with any type of body +// NewUpdatePatientCountSettingsRequestWithBody constructs an http.Request for the UpdatePatientCountSettings method, with any body, and a specified content type func NewUpdatePatientCountSettingsRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6305,7 +7098,7 @@ func NewUpdatePatientCountSettingsRequestWithBody(server string, clinicId Clinic return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -6326,13 +7119,13 @@ func NewCreateSiteRequest(server string, clinicId ClinicId, body CreateSiteJSONR return NewCreateSiteRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewCreateSiteRequestWithBody generates requests for CreateSite with any type of body +// NewCreateSiteRequestWithBody constructs an http.Request for the CreateSite method, with any body, and a specified content type func NewCreateSiteRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6352,7 +7145,7 @@ func NewCreateSiteRequestWithBody(server string, clinicId ClinicId, contentType return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -6362,20 +7155,20 @@ func NewCreateSiteRequestWithBody(server string, clinicId ClinicId, contentType return req, nil } -// NewDeleteSiteRequest generates requests for DeleteSite +// NewDeleteSiteRequest constructs an http.Request for the DeleteSite method func NewDeleteSiteRequest(server string, clinicId ClinicId, siteId SiteId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "siteId", runtime.ParamLocationPath, siteId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "siteId", siteId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6395,7 +7188,7 @@ func NewDeleteSiteRequest(server string, clinicId ClinicId, siteId SiteId) (*htt return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -6414,20 +7207,20 @@ func NewUpdateSiteRequest(server string, clinicId ClinicId, siteId SiteId, body return NewUpdateSiteRequestWithBody(server, clinicId, siteId, "application/json", bodyReader) } -// NewUpdateSiteRequestWithBody generates requests for UpdateSite with any type of body +// NewUpdateSiteRequestWithBody constructs an http.Request for the UpdateSite method, with any body, and a specified content type func NewUpdateSiteRequestWithBody(server string, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "siteId", runtime.ParamLocationPath, siteId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "siteId", siteId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6447,7 +7240,7 @@ func NewUpdateSiteRequestWithBody(server string, clinicId ClinicId, siteId SiteI return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -6468,20 +7261,20 @@ func NewMergeSiteRequest(server string, clinicId ClinicId, siteId SiteId, body M return NewMergeSiteRequestWithBody(server, clinicId, siteId, "application/json", bodyReader) } -// NewMergeSiteRequestWithBody generates requests for MergeSite with any type of body +// NewMergeSiteRequestWithBody constructs an http.Request for the MergeSite method, with any body, and a specified content type func NewMergeSiteRequestWithBody(server string, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } var pathParam1 string - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "siteId", runtime.ParamLocationPath, siteId) + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "siteId", siteId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6501,7 +7294,7 @@ func NewMergeSiteRequestWithBody(server string, clinicId ClinicId, siteId SiteId return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -6522,13 +7315,13 @@ func NewUpdateSuppressedNotificationsRequest(server string, clinicId ClinicId, b return NewUpdateSuppressedNotificationsRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewUpdateSuppressedNotificationsRequestWithBody generates requests for UpdateSuppressedNotifications with any type of body +// NewUpdateSuppressedNotificationsRequestWithBody constructs an http.Request for the UpdateSuppressedNotifications method, with any body, and a specified content type func NewUpdateSuppressedNotificationsRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6548,7 +7341,7 @@ func NewUpdateSuppressedNotificationsRequestWithBody(server string, clinicId Cli return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -6558,13 +7351,13 @@ func NewUpdateSuppressedNotificationsRequestWithBody(server string, clinicId Cli return req, nil } -// NewTideReportRequest generates requests for TideReport +// NewTideReportRequest constructs an http.Request for the TideReport method func NewTideReportRequest(server string, clinicId ClinicId, params *TideReportParams) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6585,72 +7378,81 @@ func NewTideReportRequest(server string, clinicId ClinicId, params *TideReportPa } if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "period", runtime.ParamLocationQuery, params.Period); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "period", params.Period, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } - if queryFrag, err := runtime.StyleParamWithLocation("form", false, "tags", runtime.ParamLocationQuery, params.Tags); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) + if params.Tags != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", false, "tags", params.Tags, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } + } - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "lastDataCutoff", runtime.ParamLocationQuery, params.LastDataCutoff); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "lastDataCutoff", params.LastDataCutoff, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } - if queryFrag, err := runtime.StyleParamWithLocation("form", false, "categories", runtime.ParamLocationQuery, params.Categories); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) + if params.Categories != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", false, "categories", params.Categories, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } + } - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "excludeNoData", runtime.ParamLocationQuery, params.ExcludeNoData); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "excludeNoData", params.ExcludeNoData, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if params.Sites != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", false, "sites", *params.Sites, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } + } - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -6669,13 +7471,13 @@ func NewUpdateTierRequest(server string, clinicId ClinicId, body UpdateTierJSONR return NewUpdateTierRequestWithBody(server, clinicId, "application/json", bodyReader) } -// NewUpdateTierRequestWithBody generates requests for UpdateTier with any type of body +// NewUpdateTierRequestWithBody constructs an http.Request for the UpdateTier method, with any body, and a specified content type func NewUpdateTierRequestWithBody(server string, clinicId ClinicId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "clinicId", runtime.ParamLocationPath, clinicId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clinicId", clinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6695,7 +7497,7 @@ func NewUpdateTierRequestWithBody(server string, clinicId ClinicId, contentType return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -6705,7 +7507,7 @@ func NewUpdateTierRequestWithBody(server string, clinicId ClinicId, contentType return req, nil } -// NewFindPatientsRequest generates requests for FindPatients +// NewFindPatientsRequest constructs an http.Request for the FindPatients method func NewFindPatientsRequest(server string, params *FindPatientsParams) (*http.Request, error) { var err error @@ -6725,19 +7527,21 @@ func NewFindPatientsRequest(server string, params *FindPatientsParams) (*http.Re } if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string if params.Mrn != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "mrn", runtime.ParamLocationQuery, *params.Mrn); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "mrn", *params.Mrn, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -6745,15 +7549,11 @@ func NewFindPatientsRequest(server string, params *FindPatientsParams) (*http.Re if params.BirthDate != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "birthDate", runtime.ParamLocationQuery, *params.BirthDate); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "birthDate", *params.BirthDate, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -6761,15 +7561,11 @@ func NewFindPatientsRequest(server string, params *FindPatientsParams) (*http.Re if params.WorkspaceId != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "workspaceId", runtime.ParamLocationQuery, *params.WorkspaceId); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "workspaceId", *params.WorkspaceId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -6777,15 +7573,11 @@ func NewFindPatientsRequest(server string, params *FindPatientsParams) (*http.Re if params.WorkspaceIdType != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "workspaceIdType", runtime.ParamLocationQuery, *params.WorkspaceIdType); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "workspaceIdType", *params.WorkspaceIdType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -6793,15 +7585,11 @@ func NewFindPatientsRequest(server string, params *FindPatientsParams) (*http.Re if params.Offset != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "offset", runtime.ParamLocationQuery, *params.Offset); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", *params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -6809,24 +7597,23 @@ func NewFindPatientsRequest(server string, params *FindPatientsParams) (*http.Re if params.Limit != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } } - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -6834,13 +7621,13 @@ func NewFindPatientsRequest(server string, params *FindPatientsParams) (*http.Re return req, nil } -// NewSyncEHRDataForPatientRequest generates requests for SyncEHRDataForPatient +// NewSyncEHRDataForPatientRequest constructs an http.Request for the SyncEHRDataForPatient method func NewSyncEHRDataForPatientRequest(server string, patientId PatientId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6860,7 +7647,7 @@ func NewSyncEHRDataForPatientRequest(server string, patientId PatientId) (*http. return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -6879,13 +7666,13 @@ func NewUpdatePatientSummaryRequest(server string, patientId PatientId, body Upd return NewUpdatePatientSummaryRequestWithBody(server, patientId, "application/json", bodyReader) } -// NewUpdatePatientSummaryRequestWithBody generates requests for UpdatePatientSummary with any type of body +// NewUpdatePatientSummaryRequestWithBody constructs an http.Request for the UpdatePatientSummary method, with any body, and a specified content type func NewUpdatePatientSummaryRequestWithBody(server string, patientId PatientId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "patientId", runtime.ParamLocationPath, patientId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "patientId", patientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6905,7 +7692,7 @@ func NewUpdatePatientSummaryRequestWithBody(server string, patientId PatientId, return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -6915,13 +7702,13 @@ func NewUpdatePatientSummaryRequestWithBody(server string, patientId PatientId, return req, nil } -// NewListClinicsForPatientRequest generates requests for ListClinicsForPatient +// NewListClinicsForPatientRequest constructs an http.Request for the ListClinicsForPatient method func NewListClinicsForPatientRequest(server string, userId UserId, params *ListClinicsForPatientParams) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "userId", runtime.ParamLocationPath, userId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "userId", userId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -6942,19 +7729,21 @@ func NewListClinicsForPatientRequest(server string, userId UserId, params *ListC } if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string if params.Offset != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "offset", runtime.ParamLocationQuery, *params.Offset); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", *params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } @@ -6962,24 +7751,23 @@ func NewListClinicsForPatientRequest(server string, userId UserId, params *ListC if params.Limit != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } } - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -6998,13 +7786,13 @@ func NewUpdatePatientDataSourcesRequest(server string, userId UserId, body Updat return NewUpdatePatientDataSourcesRequestWithBody(server, userId, "application/json", bodyReader) } -// NewUpdatePatientDataSourcesRequestWithBody generates requests for UpdatePatientDataSources with any type of body +// NewUpdatePatientDataSourcesRequestWithBody constructs an http.Request for the UpdatePatientDataSources method, with any body, and a specified content type func NewUpdatePatientDataSourcesRequestWithBody(server string, userId UserId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "userId", runtime.ParamLocationPath, userId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "userId", userId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -7024,7 +7812,7 @@ func NewUpdatePatientDataSourcesRequestWithBody(server string, userId UserId, co return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) if err != nil { return nil, err } @@ -7045,7 +7833,7 @@ func NewProcessEHRMessageRequest(server string, body ProcessEHRMessageJSONReques return NewProcessEHRMessageRequestWithBody(server, "application/json", bodyReader) } -// NewProcessEHRMessageRequestWithBody generates requests for ProcessEHRMessage with any type of body +// NewProcessEHRMessageRequestWithBody constructs an http.Request for the ProcessEHRMessage method, with any body, and a specified content type func NewProcessEHRMessageRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error @@ -7064,7 +7852,7 @@ func NewProcessEHRMessageRequestWithBody(server string, contentType string, body return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -7085,7 +7873,7 @@ func NewMatchClinicAndPatientRequest(server string, body MatchClinicAndPatientJS return NewMatchClinicAndPatientRequestWithBody(server, "application/json", bodyReader) } -// NewMatchClinicAndPatientRequestWithBody generates requests for MatchClinicAndPatient with any type of body +// NewMatchClinicAndPatientRequestWithBody constructs an http.Request for the MatchClinicAndPatient method, with any body, and a specified content type func NewMatchClinicAndPatientRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error @@ -7104,7 +7892,7 @@ func NewMatchClinicAndPatientRequestWithBody(server string, contentType string, return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -7114,7 +7902,7 @@ func NewMatchClinicAndPatientRequestWithBody(server string, contentType string, return req, nil } -// NewVerifyEndpointRequest generates requests for VerifyEndpoint +// NewVerifyEndpointRequest constructs an http.Request for the VerifyEndpoint method func NewVerifyEndpointRequest(server string) (*http.Request, error) { var err error @@ -7133,7 +7921,7 @@ func NewVerifyEndpointRequest(server string) (*http.Request, error) { return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -7141,13 +7929,13 @@ func NewVerifyEndpointRequest(server string) (*http.Request, error) { return req, nil } -// NewDeletePatientSummaryRequest generates requests for DeletePatientSummary +// NewDeletePatientSummaryRequest constructs an http.Request for the DeletePatientSummary method func NewDeletePatientSummaryRequest(server string, summaryId SummaryId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "summaryId", runtime.ParamLocationPath, summaryId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "summaryId", summaryId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -7167,7 +7955,7 @@ func NewDeletePatientSummaryRequest(server string, summaryId SummaryId) (*http.R return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -7175,13 +7963,13 @@ func NewDeletePatientSummaryRequest(server string, summaryId SummaryId) (*http.R return req, nil } -// NewDeleteUserFromClinicsRequest generates requests for DeleteUserFromClinics +// NewDeleteUserFromClinicsRequest constructs an http.Request for the DeleteUserFromClinics method func NewDeleteUserFromClinicsRequest(server string, userId UserId) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "userId", runtime.ParamLocationPath, userId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "userId", userId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -7201,7 +7989,7 @@ func NewDeleteUserFromClinicsRequest(server string, userId UserId) (*http.Reques return nil, err } - req, err := http.NewRequest("DELETE", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) if err != nil { return nil, err } @@ -7220,13 +8008,13 @@ func NewUpdateClinicUserDetailsRequest(server string, userId UserId, body Update return NewUpdateClinicUserDetailsRequestWithBody(server, userId, "application/json", bodyReader) } -// NewUpdateClinicUserDetailsRequestWithBody generates requests for UpdateClinicUserDetails with any type of body +// NewUpdateClinicUserDetailsRequestWithBody constructs an http.Request for the UpdateClinicUserDetails method, with any body, and a specified content type func NewUpdateClinicUserDetailsRequestWithBody(server string, userId UserId, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "userId", runtime.ParamLocationPath, userId) + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "userId", userId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) if err != nil { return nil, err } @@ -7246,7 +8034,7 @@ func NewUpdateClinicUserDetailsRequestWithBody(server string, userId UserId, con return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) if err != nil { return nil, err } @@ -7256,7 +8044,7 @@ func NewUpdateClinicUserDetailsRequestWithBody(server string, userId UserId, con return req, nil } -// NewXealthNotificationRequest generates requests for XealthNotification +// NewXealthNotificationRequest constructs an http.Request for the XealthNotification method func NewXealthNotificationRequest(server string) (*http.Request, error) { var err error @@ -7275,7 +8063,7 @@ func NewXealthNotificationRequest(server string) (*http.Request, error) { return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -7283,7 +8071,7 @@ func NewXealthNotificationRequest(server string) (*http.Request, error) { return req, nil } -// NewXealthPreorderRequest generates requests for XealthPreorder +// NewXealthPreorderRequest constructs an http.Request for the XealthPreorder method func NewXealthPreorderRequest(server string) (*http.Request, error) { var err error @@ -7302,7 +8090,7 @@ func NewXealthPreorderRequest(server string) (*http.Request, error) { return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) if err != nil { return nil, err } @@ -7310,7 +8098,7 @@ func NewXealthPreorderRequest(server string) (*http.Request, error) { return req, nil } -// NewXealthGetProgramUrlRequest generates requests for XealthGetProgramUrl +// NewXealthGetProgramUrlRequest constructs an http.Request for the XealthGetProgramUrl method func NewXealthGetProgramUrlRequest(server string) (*http.Request, error) { var err error @@ -7329,7 +8117,7 @@ func NewXealthGetProgramUrlRequest(server string) (*http.Request, error) { return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), nil) if err != nil { return nil, err } @@ -7337,7 +8125,7 @@ func NewXealthGetProgramUrlRequest(server string) (*http.Request, error) { return req, nil } -// NewXealthGetProgramsRequest generates requests for XealthGetPrograms +// NewXealthGetProgramsRequest constructs an http.Request for the XealthGetPrograms method func NewXealthGetProgramsRequest(server string) (*http.Request, error) { var err error @@ -7356,7 +8144,7 @@ func NewXealthGetProgramsRequest(server string) (*http.Request, error) { return nil, err } - req, err := http.NewRequest("PUT", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodPut, queryURL.String(), nil) if err != nil { return nil, err } @@ -7364,7 +8152,7 @@ func NewXealthGetProgramsRequest(server string) (*http.Request, error) { return req, nil } -// NewViewPDFReportRequest generates requests for ViewPDFReport +// NewViewPDFReportRequest constructs an http.Request for the ViewPDFReport method func NewViewPDFReportRequest(server string, params *ViewPDFReportParams) (*http.Request, error) { var err error @@ -7384,48 +8172,45 @@ func NewViewPDFReportRequest(server string, params *ViewPDFReportParams) (*http. } if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "clinicId", runtime.ParamLocationQuery, params.ClinicId); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "clinicId", params.ClinicId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "patientId", runtime.ParamLocationQuery, params.PatientId); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "patientId", params.PatientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "restricted_token", runtime.ParamLocationQuery, params.RestrictedToken); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "restricted_token", params.RestrictedToken, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) } } - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) if err != nil { return nil, err } @@ -7476,308 +8261,1029 @@ func WithBaseURL(baseURL string) ClientOption { // ClientWithResponsesInterface is the interface specification for the client with responses above. type ClientWithResponsesInterface interface { - // ListAllCliniciansWithResponse request + + // ListAllCliniciansWithResponse List All Clinicians + // + // Returns list of all clinician/clinic relationships in the system. Internal endpoint used for reporting purposes. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinicians (the `ListAllClinicians` operationId). ListAllCliniciansWithResponse(ctx context.Context, params *ListAllCliniciansParams, reqEditors ...RequestEditorFn) (*ListAllCliniciansResponse, error) - // ListClinicsForClinicianWithResponse request + // ListClinicsForClinicianWithResponse List Clinics for Clinician + // + // Returns a list of all clinics a clinician is a member of. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinicians/{userId}/clinics (the `ListClinicsForClinician` operationId). ListClinicsForClinicianWithResponse(ctx context.Context, userId UserId, params *ListClinicsForClinicianParams, reqEditors ...RequestEditorFn) (*ListClinicsForClinicianResponse, error) - // EnableNewClinicExperienceWithResponse request + // EnableNewClinicExperienceWithResponse Enable Clinics + // + // Internal only endpoint to enable new clinic experience for a legacy clinician account. Patients won't be migrated until the clinic details are populated and migration is explicitly kicked-off by blip. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinicians/{userId}/migrate (the `EnableNewClinicExperience` operationId). EnableNewClinicExperienceWithResponse(ctx context.Context, userId string, reqEditors ...RequestEditorFn) (*EnableNewClinicExperienceResponse, error) - // ListClinicsWithResponse request + // ListClinicsWithResponse List Clinics + // + // Retrieve the list of clinics. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics (the `ListClinics` operationId). ListClinicsWithResponse(ctx context.Context, params *ListClinicsParams, reqEditors ...RequestEditorFn) (*ListClinicsResponse, error) - // CreateClinicWithBodyWithResponse request with any body + // CreateClinicWithBodyWithResponse Create Clinic + // + // Create a new clinic. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics (the `CreateClinic` operationId). CreateClinicWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateClinicResponse, error) + // CreateClinicWithResponse Create Clinic + // + // Create a new clinic. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics (the `CreateClinic` operationId). CreateClinicWithResponse(ctx context.Context, body CreateClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateClinicResponse, error) - // GetClinicByShareCodeWithResponse request + // GetClinicByShareCodeWithResponse Get Clinic by Share Code + // + // Retrieve a clinic object with a share code. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/share_code/{shareCode} (the `GetClinicByShareCode` operationId). GetClinicByShareCodeWithResponse(ctx context.Context, shareCode string, reqEditors ...RequestEditorFn) (*GetClinicByShareCodeResponse, error) - // DeleteClinicWithResponse request + // DeleteClinicWithResponse Delete Clinic + // + // Deletes a clinic if there are no connected patient accounts (besides demo accounts). + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/clinics/{clinicId} (the `DeleteClinic` operationId). DeleteClinicWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*DeleteClinicResponse, error) - // GetClinicWithResponse request + // GetClinicWithResponse Get Clinic + // + // Retrieve a clinic by id. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId} (the `GetClinic` operationId). GetClinicWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetClinicResponse, error) - // UpdateClinicWithBodyWithResponse request with any body + // UpdateClinicWithBodyWithResponse Update Clinic + // + // Update an existing clinic. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId} (the `UpdateClinic` operationId). UpdateClinicWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateClinicResponse, error) + // UpdateClinicWithResponse Update Clinic + // + // Update an existing clinic. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId} (the `UpdateClinic` operationId). UpdateClinicWithResponse(ctx context.Context, clinicId ClinicId, body UpdateClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateClinicResponse, error) - // ListCliniciansWithResponse request + // ListCliniciansWithResponse List Clinicians + // + // Retrieve the list of clinic members. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/clinicians (the `ListClinicians` operationId). ListCliniciansWithResponse(ctx context.Context, clinicId ClinicId, params *ListCliniciansParams, reqEditors ...RequestEditorFn) (*ListCliniciansResponse, error) - // CreateClinicianWithBodyWithResponse request with any body + // CreateClinicianWithBodyWithResponse Create Clinician + // + // Internal endpoint to create a new clinician. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/clinicians (the `CreateClinician` operationId). CreateClinicianWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateClinicianResponse, error) + // CreateClinicianWithResponse Create Clinician + // + // Internal endpoint to create a new clinician. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/clinicians (the `CreateClinician` operationId). CreateClinicianWithResponse(ctx context.Context, clinicId ClinicId, body CreateClinicianJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateClinicianResponse, error) - // DeleteClinicianWithResponse request + // DeleteClinicianWithResponse Delete Clinician + // + // Removes a clinician from a clinic. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `DeleteClinician` operationId). DeleteClinicianWithResponse(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, reqEditors ...RequestEditorFn) (*DeleteClinicianResponse, error) - // GetClinicianWithResponse request + // GetClinicianWithResponse Get Clinician + // + // Retrieve a member of clinic given their user id. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `GetClinician` operationId). GetClinicianWithResponse(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, reqEditors ...RequestEditorFn) (*GetClinicianResponse, error) - // UpdateClinicianWithBodyWithResponse request with any body + // UpdateClinicianWithBodyWithResponse Update Clinician + // + // Update existing clinician. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `UpdateClinician` operationId). UpdateClinicianWithBodyWithResponse(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateClinicianResponse, error) + // UpdateClinicianWithResponse Update Clinician + // + // Update existing clinician. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `UpdateClinician` operationId). UpdateClinicianWithResponse(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, body UpdateClinicianJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateClinicianResponse, error) - // SyncEHRDataWithResponse request + // SyncEHRDataWithResponse Sync EHR Data + // + // An internal endpoint which will push the latest patient summary statistics and PDF reports for all patients who have an active subscription. The actual data is pushed asynchronously. A successful response means that an asynchronous task has been scheduled for each patient of the clinic with an active subscription. + // + // Will return 404 Not Found if the clinic does not have an active EHR connection. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/ehr/sync (the `SyncEHRData` operationId). SyncEHRDataWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*SyncEHRDataResponse, error) - // DeleteInvitedClinicianWithResponse request + // DeleteInvitedClinicianWithResponse Delete Invited Clinician + // + // Internal endpoint to delete an invited clinician object. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `DeleteInvitedClinician` operationId). DeleteInvitedClinicianWithResponse(ctx context.Context, clinicId ClinicId, inviteId InviteId, reqEditors ...RequestEditorFn) (*DeleteInvitedClinicianResponse, error) - // GetInvitedClinicianWithResponse request + // GetInvitedClinicianWithResponse Get Invited Clinician + // + // Internal endpoint to retrieve invited clinician. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `GetInvitedClinician` operationId). GetInvitedClinicianWithResponse(ctx context.Context, clinicId ClinicId, inviteId InviteId, reqEditors ...RequestEditorFn) (*GetInvitedClinicianResponse, error) - // AssociateClinicianToUserWithBodyWithResponse request with any body + // AssociateClinicianToUserWithBodyWithResponse Associate Clinician to User + // + // Internal endpoint to associate a clinician to a user. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PATCH /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `AssociateClinicianToUser` operationId). AssociateClinicianToUserWithBodyWithResponse(ctx context.Context, clinicId ClinicId, inviteId InviteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AssociateClinicianToUserResponse, error) + // AssociateClinicianToUserWithResponse Associate Clinician to User + // + // Internal endpoint to associate a clinician to a user. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PATCH /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `AssociateClinicianToUser` operationId). AssociateClinicianToUserWithResponse(ctx context.Context, clinicId ClinicId, inviteId InviteId, body AssociateClinicianToUserJSONRequestBody, reqEditors ...RequestEditorFn) (*AssociateClinicianToUserResponse, error) - // ListMembershipRestrictionsWithResponse request + // ListMembershipRestrictionsWithResponse List Membership Restrictions + // + // Returns a list of the membership restrictions that will be evaluated when a user joins a clinic. + // + // Only clinic admins can access this endpoint. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/membership_restrictions (the `ListMembershipRestrictions` operationId). ListMembershipRestrictionsWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*ListMembershipRestrictionsResponse, error) - // UpdateMembershipRestrictionsWithBodyWithResponse request with any body + // UpdateMembershipRestrictionsWithBodyWithResponse Update Membership Restrictions + // + // An internal endpoint to update clinic membership restrictions. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/membership_restrictions (the `UpdateMembershipRestrictions` operationId). UpdateMembershipRestrictionsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateMembershipRestrictionsResponse, error) + // UpdateMembershipRestrictionsWithResponse Update Membership Restrictions + // + // An internal endpoint to update clinic membership restrictions. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/membership_restrictions (the `UpdateMembershipRestrictions` operationId). UpdateMembershipRestrictionsWithResponse(ctx context.Context, clinicId ClinicId, body UpdateMembershipRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateMembershipRestrictionsResponse, error) - // MergeClinicWithBodyWithResponse request with any body + // MergeClinicWithBodyWithResponse Merge Clinic + // + // Merges tags, patients, clinicians, invites and share codes of the source clinic + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/merge (the `MergeClinic` operationId). MergeClinicWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MergeClinicResponse, error) + // MergeClinicWithResponse Merge Clinic + // + // Merges tags, patients, clinicians, invites and share codes of the source clinic + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/merge (the `MergeClinic` operationId). MergeClinicWithResponse(ctx context.Context, clinicId ClinicId, body MergeClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*MergeClinicResponse, error) - // TriggerInitialMigrationWithBodyWithResponse request with any body + // TriggerInitialMigrationWithBodyWithResponse Trigger initial migration + // + // Trigger the initial migration. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/migrate (the `TriggerInitialMigration` operationId). TriggerInitialMigrationWithBodyWithResponse(ctx context.Context, clinicId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*TriggerInitialMigrationResponse, error) + // TriggerInitialMigrationWithResponse Trigger initial migration + // + // Trigger the initial migration. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/migrate (the `TriggerInitialMigration` operationId). TriggerInitialMigrationWithResponse(ctx context.Context, clinicId string, body TriggerInitialMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*TriggerInitialMigrationResponse, error) - // ListMigrationsWithResponse request + // ListMigrationsWithResponse Retrieve Migration Status + // + // ListMigrations. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/migrations (the `ListMigrations` operationId). ListMigrationsWithResponse(ctx context.Context, clinicId string, reqEditors ...RequestEditorFn) (*ListMigrationsResponse, error) - // MigrateLegacyClinicianPatientsWithBodyWithResponse request with any body + // MigrateLegacyClinicianPatientsWithBodyWithResponse Migrate Legacy Clinician Patients + // + // Migrate the patients of a legacy clinic account. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/migrations (the `MigrateLegacyClinicianPatients` operationId). MigrateLegacyClinicianPatientsWithBodyWithResponse(ctx context.Context, clinicId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MigrateLegacyClinicianPatientsResponse, error) + // MigrateLegacyClinicianPatientsWithResponse Migrate Legacy Clinician Patients + // + // Migrate the patients of a legacy clinic account. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/migrations (the `MigrateLegacyClinicianPatients` operationId). MigrateLegacyClinicianPatientsWithResponse(ctx context.Context, clinicId string, body MigrateLegacyClinicianPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*MigrateLegacyClinicianPatientsResponse, error) - // GetMigrationWithResponse request + // GetMigrationWithResponse Get Migration + // + // Get a migration by user id. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/migrations/{userId} (the `GetMigration` operationId). GetMigrationWithResponse(ctx context.Context, clinicId ClinicIdV1, userId UserId, reqEditors ...RequestEditorFn) (*GetMigrationResponse, error) - // UpdateMigrationWithBodyWithResponse request with any body + // UpdateMigrationWithBodyWithResponse Update Migration + // + // Internal endpoint to update the status of a migration. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PATCH /v1/clinics/{clinicId}/migrations/{userId} (the `UpdateMigration` operationId). UpdateMigrationWithBodyWithResponse(ctx context.Context, clinicId ClinicIdV1, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateMigrationResponse, error) + // UpdateMigrationWithResponse Update Migration + // + // Internal endpoint to update the status of a migration. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PATCH /v1/clinics/{clinicId}/migrations/{userId} (the `UpdateMigration` operationId). UpdateMigrationWithResponse(ctx context.Context, clinicId ClinicIdV1, userId UserId, body UpdateMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateMigrationResponse, error) - // GetPatientCountWithResponse request + // GetPatientCountWithResponse Get Patient Count + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/patient_count (the `GetPatientCount` operationId). GetPatientCountWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetPatientCountResponse, error) - // RefreshPatientCountWithResponse request + // RefreshPatientCountWithResponse Refresh Patient Count + // + // An internal endpoint to refresh the patient count for the specified clinic. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patient_count/refresh (the `RefreshPatientCount` operationId). RefreshPatientCountWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*RefreshPatientCountResponse, error) - // CreatePatientTagWithBodyWithResponse request with any body + // CreatePatientTagWithBodyWithResponse Create Patient Tag + // + // Create a new patient tag. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patient_tags (the `CreatePatientTag` operationId). CreatePatientTagWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreatePatientTagResponse, error) + // CreatePatientTagWithResponse Create Patient Tag + // + // Create a new patient tag. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patient_tags (the `CreatePatientTag` operationId). CreatePatientTagWithResponse(ctx context.Context, clinicId ClinicId, body CreatePatientTagJSONRequestBody, reqEditors ...RequestEditorFn) (*CreatePatientTagResponse, error) - // DeletePatientTagWithResponse request + // DeletePatientTagWithResponse Delete Patient Tag + // + // Removes a patient tag from a clinic and tagged patients. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `DeletePatientTag` operationId). DeletePatientTagWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, reqEditors ...RequestEditorFn) (*DeletePatientTagResponse, error) - // UpdatePatientTagWithBodyWithResponse request with any body + // UpdatePatientTagWithBodyWithResponse Update Patient Tag + // + // Update existing patient tag. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `UpdatePatientTag` operationId). UpdatePatientTagWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientTagResponse, error) + // UpdatePatientTagWithResponse Update Patient Tag + // + // Update existing patient tag. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `UpdatePatientTag` operationId). UpdatePatientTagWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body UpdatePatientTagJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientTagResponse, error) - // ConvertPatientTagToSiteWithResponse request + // ConvertPatientTagToSiteWithResponse Convert Patient Tag to Site + // + // Convert a patient tag to a site. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patient_tags/{patientTagId}/site (the `ConvertPatientTagToSite` operationId). ConvertPatientTagToSiteWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, reqEditors ...RequestEditorFn) (*ConvertPatientTagToSiteResponse, error) - // ListPatientsWithResponse request + // ListPatientsWithResponse List Patients + // + // Retrieve a list of patients of a clinic. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/patients (the `ListPatients` operationId). ListPatientsWithResponse(ctx context.Context, clinicId ClinicId, params *ListPatientsParams, reqEditors ...RequestEditorFn) (*ListPatientsResponse, error) - // CreatePatientAccountWithBodyWithResponse request with any body + // CreatePatientAccountWithBodyWithResponse Create Patient Account + // + // Create a custodial account for a patient. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients (the `CreatePatientAccount` operationId). CreatePatientAccountWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreatePatientAccountResponse, error) + // CreatePatientAccountWithResponse Create Patient Account + // + // Create a custodial account for a patient. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients (the `CreatePatientAccount` operationId). CreatePatientAccountWithResponse(ctx context.Context, clinicId ClinicId, body CreatePatientAccountJSONRequestBody, reqEditors ...RequestEditorFn) (*CreatePatientAccountResponse, error) - // AssignPatientTagToClinicPatientsWithBodyWithResponse request with any body + // AssignPatientTagToClinicPatientsWithBodyWithResponse Assign Patient Tag To Clinic Patients + // + // Assign a patient tag to a subset of clinic patients. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/assign_tag/{patientTagId} (the `AssignPatientTagToClinicPatients` operationId). AssignPatientTagToClinicPatientsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AssignPatientTagToClinicPatientsResponse, error) + // AssignPatientTagToClinicPatientsWithResponse Assign Patient Tag To Clinic Patients + // + // Assign a patient tag to a subset of clinic patients. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/assign_tag/{patientTagId} (the `AssignPatientTagToClinicPatients` operationId). AssignPatientTagToClinicPatientsWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body AssignPatientTagToClinicPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*AssignPatientTagToClinicPatientsResponse, error) - // DeletePatientTagFromClinicPatientsWithBodyWithResponse request with any body + // DeletePatientTagFromClinicPatientsWithBodyWithResponse Delete Patient Tag From Clinic Patients + // + // Delete a patient tag from all or a subset of clinic patients. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/delete_tag/{patientTagId} (the `DeletePatientTagFromClinicPatients` operationId). DeletePatientTagFromClinicPatientsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeletePatientTagFromClinicPatientsResponse, error) + // DeletePatientTagFromClinicPatientsWithResponse Delete Patient Tag From Clinic Patients + // + // Delete a patient tag from all or a subset of clinic patients. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/delete_tag/{patientTagId} (the `DeletePatientTagFromClinicPatients` operationId). DeletePatientTagFromClinicPatientsWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body DeletePatientTagFromClinicPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*DeletePatientTagFromClinicPatientsResponse, error) - // DeletePatientWithResponse request + // DeletePatientWithResponse Delete Patient + // + // Deletes patient from a clinic. Requires the request to be made by a clinic admin or the currently authenticated user id to match the patient id. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId} (the `DeletePatient` operationId). DeletePatientWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*DeletePatientResponse, error) - // GetPatientWithResponse request + // GetPatientWithResponse Get Patient + // + // Retrieve a patient of clinic given their user id. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/patients/{patientId} (the `GetPatient` operationId). GetPatientWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*GetPatientResponse, error) - // CreatePatientFromUserWithBodyWithResponse request with any body + // CreatePatientFromUserWithBodyWithResponse Create Patient from Existing User + // + // Internal endpoint for creating a patient from an existing user. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId} (the `CreatePatientFromUser` operationId). CreatePatientFromUserWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreatePatientFromUserResponse, error) + // CreatePatientFromUserWithResponse Create Patient from Existing User + // + // Internal endpoint for creating a patient from an existing user. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId} (the `CreatePatientFromUser` operationId). CreatePatientFromUserWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, body CreatePatientFromUserJSONRequestBody, reqEditors ...RequestEditorFn) (*CreatePatientFromUserResponse, error) - // UpdatePatientWithBodyWithResponse request with any body + // UpdatePatientWithBodyWithResponse Update Patient + // + // Update existing patient of a clinic. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId} (the `UpdatePatient` operationId). UpdatePatientWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientResponse, error) + // UpdatePatientWithResponse Update Patient + // + // Update existing patient of a clinic. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId} (the `UpdatePatient` operationId). UpdatePatientWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, body UpdatePatientJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientResponse, error) - // ConnectProviderWithResponse request + // ConnectProviderWithResponse Connect Provider + // + // Send a new request to the patient to connect a data provider. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId}/connect/{providerId} (the `ConnectProvider` operationId). ConnectProviderWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, providerId ProviderId, reqEditors ...RequestEditorFn) (*ConnectProviderResponse, error) - // UpdatePatientPermissionsWithBodyWithResponse request with any body + // UpdatePatientPermissionsWithBodyWithResponse Update Patient Permissions + // + // Update permissions that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. + // + // Only patients can change the permissions. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/permissions (the `UpdatePatientPermissions` operationId). UpdatePatientPermissionsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientPermissionsResponse, error) + // UpdatePatientPermissionsWithResponse Update Patient Permissions + // + // Update permissions that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. + // + // Only patients can change the permissions. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/permissions (the `UpdatePatientPermissions` operationId). UpdatePatientPermissionsWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, body UpdatePatientPermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientPermissionsResponse, error) - // DeletePatientPermissionWithResponse request + // DeletePatientPermissionWithResponse Delete Patient Permission + // + // Remove a single permission that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. + // + // Only patients can change the permissions. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId}/permissions/{permission} (the `DeletePatientPermission` operationId). DeletePatientPermissionWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, permission string, reqEditors ...RequestEditorFn) (*DeletePatientPermissionResponse, error) - // DeletePatientReviewsWithResponse request + // DeletePatientReviewsWithResponse Delete Patient Reviews + // + // Revert the patient's last reviewed date to the previous set. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId}/reviews (the `DeletePatientReviews` operationId). DeletePatientReviewsWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*DeletePatientReviewsResponse, error) - // UpdatePatientReviewsWithResponse request + // UpdatePatientReviewsWithResponse Update Patient Reviews + // + // Update the patient's last reviewed date and clinician ID. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/reviews (the `UpdatePatientReviews` operationId). UpdatePatientReviewsWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*UpdatePatientReviewsResponse, error) - // SendUploadReminderWithResponse request + // SendUploadReminderWithResponse Send Upload Reminder + // + // Sends upload reminder to the patient. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId}/upload_reminder (the `SendUploadReminder` operationId). SendUploadReminderWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*SendUploadReminderResponse, error) - // GenerateMergeReportWithBodyWithResponse request with any body + // GenerateMergeReportWithBodyWithResponse Generate Clinic Merge Report + // + // Generates a spreadsheet report for merging two clinics. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/reports/merge (the `GenerateMergeReport` operationId). GenerateMergeReportWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenerateMergeReportResponse, error) + // GenerateMergeReportWithResponse Generate Clinic Merge Report + // + // Generates a spreadsheet report for merging two clinics. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/reports/merge (the `GenerateMergeReport` operationId). GenerateMergeReportWithResponse(ctx context.Context, clinicId ClinicId, body GenerateMergeReportJSONRequestBody, reqEditors ...RequestEditorFn) (*GenerateMergeReportResponse, error) - // AddServiceAccountWithBodyWithResponse request with any body + // AddServiceAccountWithBodyWithResponse Add Service Account + // + // Adds authentication client's service account as a new clinician member. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/service_accounts (the `AddServiceAccount` operationId). AddServiceAccountWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AddServiceAccountResponse, error) + // AddServiceAccountWithResponse Add Service Account + // + // Adds authentication client's service account as a new clinician member. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/service_accounts (the `AddServiceAccount` operationId). AddServiceAccountWithResponse(ctx context.Context, clinicId ClinicId, body AddServiceAccountJSONRequestBody, reqEditors ...RequestEditorFn) (*AddServiceAccountResponse, error) - // GetEHRSettingsWithResponse request + // GetEHRSettingsWithResponse Get EHR Settings + // + // Returns EHR related settings. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/settings/ehr (the `GetEHRSettings` operationId). GetEHRSettingsWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetEHRSettingsResponse, error) - // UpdateEHRSettingsWithBodyWithResponse request with any body + // UpdateEHRSettingsWithBodyWithResponse Update EHR Settings + // + // Update EHR related settings for a clinic. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/ehr (the `UpdateEHRSettings` operationId). UpdateEHRSettingsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateEHRSettingsResponse, error) + // UpdateEHRSettingsWithResponse Update EHR Settings + // + // Update EHR related settings for a clinic. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/ehr (the `UpdateEHRSettings` operationId). UpdateEHRSettingsWithResponse(ctx context.Context, clinicId ClinicId, body UpdateEHRSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateEHRSettingsResponse, error) - // GetMRNSettingsWithResponse request + // GetMRNSettingsWithResponse Get MRN Settings + // + // Retrieve MRN settings. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/settings/mrn (the `GetMRNSettings` operationId). GetMRNSettingsWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetMRNSettingsResponse, error) - // UpdateMRNSettingsWithBodyWithResponse request with any body + // UpdateMRNSettingsWithBodyWithResponse Update MRN Settings + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/mrn (the `UpdateMRNSettings` operationId). UpdateMRNSettingsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateMRNSettingsResponse, error) + // UpdateMRNSettingsWithResponse Update MRN Settings + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/mrn (the `UpdateMRNSettings` operationId). UpdateMRNSettingsWithResponse(ctx context.Context, clinicId ClinicId, body UpdateMRNSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateMRNSettingsResponse, error) - // GetPatientCountSettingsWithResponse request + // GetPatientCountSettingsWithResponse Get Patient Count Settings + // + // Get Patient Count settings. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/settings/patient_count (the `GetPatientCountSettings` operationId). GetPatientCountSettingsWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetPatientCountSettingsResponse, error) - // UpdatePatientCountSettingsWithBodyWithResponse request with any body + // UpdatePatientCountSettingsWithBodyWithResponse Update Patient Count Settings + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/patient_count (the `UpdatePatientCountSettings` operationId). UpdatePatientCountSettingsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientCountSettingsResponse, error) + // UpdatePatientCountSettingsWithResponse Update Patient Count Settings + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/settings/patient_count (the `UpdatePatientCountSettings` operationId). UpdatePatientCountSettingsWithResponse(ctx context.Context, clinicId ClinicId, body UpdatePatientCountSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientCountSettingsResponse, error) - // CreateSiteWithBodyWithResponse request with any body + // CreateSiteWithBodyWithResponse Create a Site + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/sites (the `CreateSite` operationId). CreateSiteWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateSiteResponse, error) + // CreateSiteWithResponse Create a Site + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/sites (the `CreateSite` operationId). CreateSiteWithResponse(ctx context.Context, clinicId ClinicId, body CreateSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateSiteResponse, error) - // DeleteSiteWithResponse request + // DeleteSiteWithResponse Delete a Site + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/clinics/{clinicId}/sites/{siteId} (the `DeleteSite` operationId). DeleteSiteWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, reqEditors ...RequestEditorFn) (*DeleteSiteResponse, error) - // UpdateSiteWithBodyWithResponse request with any body + // UpdateSiteWithBodyWithResponse Update a Site + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/sites/{siteId} (the `UpdateSite` operationId). UpdateSiteWithBodyWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateSiteResponse, error) + // UpdateSiteWithResponse Update a Site + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/clinics/{clinicId}/sites/{siteId} (the `UpdateSite` operationId). UpdateSiteWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, body UpdateSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateSiteResponse, error) - // MergeSiteWithBodyWithResponse request with any body + // MergeSiteWithBodyWithResponse Merge two sites + // + // Merge a site, identified in the request body, into the site identified in the URL. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/sites/{siteId}/merge (the `MergeSite` operationId). MergeSiteWithBodyWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MergeSiteResponse, error) + // MergeSiteWithResponse Merge two sites + // + // Merge a site, identified in the request body, into the site identified in the URL. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/sites/{siteId}/merge (the `MergeSite` operationId). MergeSiteWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, body MergeSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*MergeSiteResponse, error) - // UpdateSuppressedNotificationsWithBodyWithResponse request with any body + // UpdateSuppressedNotificationsWithBodyWithResponse Update Suppressed Notifications + // + // Endpoint to update clinic suppressed notifications. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/suppressed_notifications (the `UpdateSuppressedNotifications` operationId). UpdateSuppressedNotificationsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateSuppressedNotificationsResponse, error) + // UpdateSuppressedNotificationsWithResponse Update Suppressed Notifications + // + // Endpoint to update clinic suppressed notifications. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/suppressed_notifications (the `UpdateSuppressedNotifications` operationId). UpdateSuppressedNotificationsWithResponse(ctx context.Context, clinicId ClinicId, body UpdateSuppressedNotificationsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateSuppressedNotificationsResponse, error) - // TideReportWithResponse request + // TideReportWithResponse View TIDE Report + // + // Retrieve a report of patients with tags by TIDE categories. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/clinics/{clinicId}/tide_report (the `TideReport` operationId). TideReportWithResponse(ctx context.Context, clinicId ClinicId, params *TideReportParams, reqEditors ...RequestEditorFn) (*TideReportResponse, error) - // UpdateTierWithBodyWithResponse request with any body + // UpdateTierWithBodyWithResponse Update Tier + // + // Internal endpoint to update clinic service tier. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/tier (the `UpdateTier` operationId). UpdateTierWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateTierResponse, error) + // UpdateTierWithResponse Update Tier + // + // Internal endpoint to update clinic service tier. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/clinics/{clinicId}/tier (the `UpdateTier` operationId). UpdateTierWithResponse(ctx context.Context, clinicId ClinicId, body UpdateTierJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateTierResponse, error) - // FindPatientsWithResponse request + // FindPatientsWithResponse Find Patients + // + // Returns all patients to which the clinician has access to. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/patients (the `FindPatients` operationId). FindPatientsWithResponse(ctx context.Context, params *FindPatientsParams, reqEditors ...RequestEditorFn) (*FindPatientsResponse, error) - // SyncEHRDataForPatientWithResponse request + // SyncEHRDataForPatientWithResponse Sync EHR Data for Patient + // + // An internal endpoint which will push the latest patient summary statistics and PDF reports to all EHR enabled clinics for which the patient has an active subscription. The actual data is pushed asynchronously. A successful response means that an asynchronous task has been scheduled. + // + // A 404 response will be returned if no active subscriptions can be found for given patient. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/patients/{patientId}/ehr/sync (the `SyncEHRDataForPatient` operationId). SyncEHRDataForPatientWithResponse(ctx context.Context, patientId PatientId, reqEditors ...RequestEditorFn) (*SyncEHRDataForPatientResponse, error) - // UpdatePatientSummaryWithBodyWithResponse request with any body + // UpdatePatientSummaryWithBodyWithResponse UpdatePatientSummary + // + // Internal endpoint which updates the summary in all patient records which match the user id. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/patients/{patientId}/summary (the `UpdatePatientSummary` operationId). UpdatePatientSummaryWithBodyWithResponse(ctx context.Context, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientSummaryResponse, error) + // UpdatePatientSummaryWithResponse UpdatePatientSummary + // + // Internal endpoint which updates the summary in all patient records which match the user id. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/patients/{patientId}/summary (the `UpdatePatientSummary` operationId). UpdatePatientSummaryWithResponse(ctx context.Context, patientId PatientId, body UpdatePatientSummaryJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientSummaryResponse, error) - // ListClinicsForPatientWithResponse request + // ListClinicsForPatientWithResponse List Clinics for Patient + // + // Get all clinics a user is a patient of. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/patients/{userId}/clinics (the `ListClinicsForPatient` operationId). ListClinicsForPatientWithResponse(ctx context.Context, userId UserId, params *ListClinicsForPatientParams, reqEditors ...RequestEditorFn) (*ListClinicsForPatientResponse, error) - // UpdatePatientDataSourcesWithBodyWithResponse request with any body + // UpdatePatientDataSourcesWithBodyWithResponse Create or update a data source for a patient + // + // Internal endpoint to upsert patient data source provider details in all their associated clinic records. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/patients/{userId}/data_sources (the `UpdatePatientDataSources` operationId). UpdatePatientDataSourcesWithBodyWithResponse(ctx context.Context, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientDataSourcesResponse, error) + // UpdatePatientDataSourcesWithResponse Create or update a data source for a patient + // + // Internal endpoint to upsert patient data source provider details in all their associated clinic records. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/patients/{userId}/data_sources (the `UpdatePatientDataSources` operationId). UpdatePatientDataSourcesWithResponse(ctx context.Context, userId UserId, body UpdatePatientDataSourcesJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientDataSourcesResponse, error) - // ProcessEHRMessageWithBodyWithResponse request with any body + // ProcessEHRMessageWithBodyWithResponse Redox EHR Endpoint + // + // An endpoint for incoming Redox messages. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/redox (the `ProcessEHRMessage` operationId). ProcessEHRMessageWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ProcessEHRMessageResponse, error) + // ProcessEHRMessageWithResponse Redox EHR Endpoint + // + // An endpoint for incoming Redox messages. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/redox (the `ProcessEHRMessage` operationId). ProcessEHRMessageWithResponse(ctx context.Context, body ProcessEHRMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*ProcessEHRMessageResponse, error) - // MatchClinicAndPatientWithBodyWithResponse request with any body + // MatchClinicAndPatientWithBodyWithResponse Match Clinic and Patient + // + // An internal endpoint used for matching an EHR request (e.g. NewOrder) to a clinic and patient. + // + // If a clinic with the provided details cannot be found, the service will return `404 Not Found` and will not attempt to match patients to the provided details. + // + // Due to lack of uniqueness constraints on the MRN and DOB fields it's possible that multiple patient records match the provided attributes. All results will be returned in the response and it's the calling services responsiblity to determine how to handle duplicates records. + // + // If a unique match is found, the patient matching criteria will be added to the patient record for future use (e.g. pushing data on a schedule). + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/redox/match (the `MatchClinicAndPatient` operationId). MatchClinicAndPatientWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MatchClinicAndPatientResponse, error) + // MatchClinicAndPatientWithResponse Match Clinic and Patient + // + // An internal endpoint used for matching an EHR request (e.g. NewOrder) to a clinic and patient. + // + // If a clinic with the provided details cannot be found, the service will return `404 Not Found` and will not attempt to match patients to the provided details. + // + // Due to lack of uniqueness constraints on the MRN and DOB fields it's possible that multiple patient records match the provided attributes. All results will be returned in the response and it's the calling services responsiblity to determine how to handle duplicates records. + // + // If a unique match is found, the patient matching criteria will be added to the patient record for future use (e.g. pushing data on a schedule). + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/redox/match (the `MatchClinicAndPatient` operationId). MatchClinicAndPatientWithResponse(ctx context.Context, body MatchClinicAndPatientJSONRequestBody, reqEditors ...RequestEditorFn) (*MatchClinicAndPatientResponse, error) - // VerifyEndpointWithResponse request + // VerifyEndpointWithResponse Redox Verify Endpoint + // + // Internal endpoint for Redox challenge verification. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/redox/verify (the `VerifyEndpoint` operationId). VerifyEndpointWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*VerifyEndpointResponse, error) - // DeletePatientSummaryWithResponse request + // DeletePatientSummaryWithResponse DeletePatientSummary + // + // Internal endpoint which deletes the summary in all patient records which match the summary id. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/summaries/{summaryId}/clinics (the `DeletePatientSummary` operationId). DeletePatientSummaryWithResponse(ctx context.Context, summaryId SummaryId, reqEditors ...RequestEditorFn) (*DeletePatientSummaryResponse, error) - // DeleteUserFromClinicsWithResponse request + // DeleteUserFromClinicsWithResponse Remove User from Clinics + // + // Internal endpoint that deletes any user associations with clinics they are a patient or a member of. + // + // If the user is a patient of a clinic the service will delete the patient-clinic relationship. + // + // If the user is one of many admins of any clinic the service will delete the clinician-clinic relationship. + // + // If the user is the only admin of a clinic the service will: + // - delete the clinician record, leaving the clinic without an admin + // - delete all non-custodial patient relationships of the clinic + // - keep all custodian relationships, allowing users to claim custodial accounts with an email + // - keep all custodian accounts and data even if they can’t be claimed when there isn’t an email address associated with the account + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /v1/users/{userId}/clinics (the `DeleteUserFromClinics` operationId). DeleteUserFromClinicsWithResponse(ctx context.Context, userId UserId, reqEditors ...RequestEditorFn) (*DeleteUserFromClinicsResponse, error) - // UpdateClinicUserDetailsWithBodyWithResponse request with any body + // UpdateClinicUserDetailsWithBodyWithResponse Update User Details + // + // Internal endpoint to update clinician details. In the future it may be extended to support patient updates. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/users/{userId}/clinics (the `UpdateClinicUserDetails` operationId). UpdateClinicUserDetailsWithBodyWithResponse(ctx context.Context, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateClinicUserDetailsResponse, error) + // UpdateClinicUserDetailsWithResponse Update User Details + // + // Internal endpoint to update clinician details. In the future it may be extended to support patient updates. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/users/{userId}/clinics (the `UpdateClinicUserDetails` operationId). UpdateClinicUserDetailsWithResponse(ctx context.Context, userId UserId, body UpdateClinicUserDetailsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateClinicUserDetailsResponse, error) - // XealthNotificationWithResponse request + // XealthNotificationWithResponse Notification Webhook + // + // Notification webhook. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/xealth/notification (the `XealthNotification` operationId). XealthNotificationWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*XealthNotificationResponse, error) - // XealthPreorderWithResponse request + // XealthPreorderWithResponse Preorder Form Webhook + // + // The Preorder endpoint can be invoked when a care provider attempts to place an order manually within the EHR. Subscribers can use Preorder Forms to present additional information to, or receive input from, a care provider before an order can be placed. Preorder Forms can also be implemented so as to prevent an order from being placed entirely. + // + // https://sandbox.xealth.io/app/api-docs#tag/Digital-Care/paths/preorder/post + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /v1/xealth/preorder (the `XealthPreorder` operationId). XealthPreorderWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*XealthPreorderResponse, error) - // XealthGetProgramUrlWithResponse request + // XealthGetProgramUrlWithResponse Get Program Url + // + // Get program URL. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/xealth/program (the `XealthGetProgramUrl` operationId). XealthGetProgramUrlWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*XealthGetProgramUrlResponse, error) - // XealthGetProgramsWithResponse request + // XealthGetProgramsWithResponse Get Programs + // + // Get programs. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /v1/xealth/programs (the `XealthGetPrograms` operationId). XealthGetProgramsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*XealthGetProgramsResponse, error) - // ViewPDFReportWithResponse request + // ViewPDFReportWithResponse View PDF Report + // + // View PDF report for a given patient of a clinic. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /v1/xealth/report/web/viewer.html (the `ViewPDFReport` operationId). ViewPDFReportWithResponse(ctx context.Context, params *ViewPDFReportParams, reqEditors ...RequestEditorFn) (*ViewPDFReportResponse, error) } type ListAllCliniciansResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *[]ClinicianClinicRelationshipV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *[]ClinicianClinicRelationshipV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ListAllCliniciansResponse) GetJSON200() *[]ClinicianClinicRelationshipV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r ListAllCliniciansResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -7796,10 +9302,29 @@ func (r ListAllCliniciansResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListAllCliniciansResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ListClinicsForClinicianResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ClinicianClinicRelationshipsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicianClinicRelationshipsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ListClinicsForClinicianResponse) GetJSON200() *ClinicianClinicRelationshipsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r ListClinicsForClinicianResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -7818,10 +9343,29 @@ func (r ListClinicsForClinicianResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListClinicsForClinicianResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type EnableNewClinicExperienceResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ClinicV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r EnableNewClinicExperienceResponse) GetJSON200() *ClinicV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r EnableNewClinicExperienceResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -7840,13 +9384,50 @@ func (r EnableNewClinicExperienceResponse) StatusCode() int { return 0 } -type ListClinicsResponse struct { - Body []byte +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r EnableNewClinicExperienceResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ListClinicsResponse struct { + Body []byte HTTPResponse *http.Response - JSON200 *ClinicsV1 - JSON400 *ErrorV1 - JSON403 *ErrorV1 - JSON500 *ErrorV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicsV1 + // JSON400 the response for an HTTP 400 `application/json` response + JSON400 *ErrorV1 + // JSON403 the response for an HTTP 403 `application/json` response + JSON403 *ErrorV1 + // JSON500 the response for an HTTP 500 `application/json` response + JSON500 *ErrorV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ListClinicsResponse) GetJSON200() *ClinicsV1 { + return r.JSON200 +} + +// GetJSON400 returns the response for an HTTP 400 `application/json` response +func (r ListClinicsResponse) GetJSON400() *ErrorV1 { + return r.JSON400 +} + +// GetJSON403 returns the response for an HTTP 403 `application/json` response +func (r ListClinicsResponse) GetJSON403() *ErrorV1 { + return r.JSON403 +} + +// GetJSON500 returns the response for an HTTP 500 `application/json` response +func (r ListClinicsResponse) GetJSON500() *ErrorV1 { + return r.JSON500 +} + +// GetBody returns the raw response body bytes +func (r ListClinicsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -7865,11 +9446,36 @@ func (r ListClinicsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListClinicsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type CreateClinicResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ClinicV1 - XML200 *map[string]interface{} + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicV1 + // XML200 the response for an HTTP 200 `application/xml` response + XML200 *map[string]interface{} +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r CreateClinicResponse) GetJSON200() *ClinicV1 { + return r.JSON200 +} + +// GetXML200 returns the response for an HTTP 200 `application/xml` response +func (r CreateClinicResponse) GetXML200() *map[string]interface{} { + return r.XML200 +} + +// GetBody returns the raw response body bytes +func (r CreateClinicResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -7888,10 +9494,29 @@ func (r CreateClinicResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreateClinicResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetClinicByShareCodeResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ClinicV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetClinicByShareCodeResponse) GetJSON200() *ClinicV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetClinicByShareCodeResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -7910,11 +9535,24 @@ func (r GetClinicByShareCodeResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetClinicByShareCodeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeleteClinicResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeleteClinicResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeleteClinicResponse) Status() string { if r.HTTPResponse != nil { @@ -7931,10 +9569,29 @@ func (r DeleteClinicResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteClinicResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetClinicResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ClinicV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetClinicResponse) GetJSON200() *ClinicV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetClinicResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -7953,10 +9610,29 @@ func (r GetClinicResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetClinicResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateClinicResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ClinicV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpdateClinicResponse) GetJSON200() *ClinicV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r UpdateClinicResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -7975,10 +9651,29 @@ func (r UpdateClinicResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateClinicResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ListCliniciansResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *CliniciansV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *CliniciansV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ListCliniciansResponse) GetJSON200() *CliniciansV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r ListCliniciansResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -7997,11 +9692,24 @@ func (r ListCliniciansResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListCliniciansResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type CreateClinicianResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r CreateClinicianResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r CreateClinicianResponse) Status() string { if r.HTTPResponse != nil { @@ -8018,11 +9726,24 @@ func (r CreateClinicianResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreateClinicianResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeleteClinicianResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeleteClinicianResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeleteClinicianResponse) Status() string { if r.HTTPResponse != nil { @@ -8039,10 +9760,29 @@ func (r DeleteClinicianResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteClinicianResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetClinicianResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ClinicianV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicianV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetClinicianResponse) GetJSON200() *ClinicianV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetClinicianResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8061,11 +9801,24 @@ func (r GetClinicianResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetClinicianResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateClinicianResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r UpdateClinicianResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r UpdateClinicianResponse) Status() string { if r.HTTPResponse != nil { @@ -8082,11 +9835,24 @@ func (r UpdateClinicianResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateClinicianResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type SyncEHRDataResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r SyncEHRDataResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r SyncEHRDataResponse) Status() string { if r.HTTPResponse != nil { @@ -8103,11 +9869,24 @@ func (r SyncEHRDataResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SyncEHRDataResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeleteInvitedClinicianResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeleteInvitedClinicianResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeleteInvitedClinicianResponse) Status() string { if r.HTTPResponse != nil { @@ -8124,10 +9903,29 @@ func (r DeleteInvitedClinicianResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteInvitedClinicianResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetInvitedClinicianResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ClinicianV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicianV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetInvitedClinicianResponse) GetJSON200() *ClinicianV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetInvitedClinicianResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8146,10 +9944,29 @@ func (r GetInvitedClinicianResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetInvitedClinicianResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type AssociateClinicianToUserResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *ClinicianV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ClinicianV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r AssociateClinicianToUserResponse) GetJSON200() *ClinicianV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r AssociateClinicianToUserResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8168,10 +9985,29 @@ func (r AssociateClinicianToUserResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r AssociateClinicianToUserResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ListMembershipRestrictionsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *MembershipRestrictionsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *MembershipRestrictionsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ListMembershipRestrictionsResponse) GetJSON200() *MembershipRestrictionsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r ListMembershipRestrictionsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8190,10 +10026,29 @@ func (r ListMembershipRestrictionsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListMembershipRestrictionsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateMembershipRestrictionsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *MembershipRestrictionsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *MembershipRestrictionsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpdateMembershipRestrictionsResponse) GetJSON200() *MembershipRestrictionsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r UpdateMembershipRestrictionsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8212,11 +10067,24 @@ func (r UpdateMembershipRestrictionsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateMembershipRestrictionsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type MergeClinicResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r MergeClinicResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r MergeClinicResponse) Status() string { if r.HTTPResponse != nil { @@ -8233,10 +10101,29 @@ func (r MergeClinicResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r MergeClinicResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type TriggerInitialMigrationResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *MigrationV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *MigrationV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r TriggerInitialMigrationResponse) GetJSON200() *MigrationV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r TriggerInitialMigrationResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8255,10 +10142,29 @@ func (r TriggerInitialMigrationResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r TriggerInitialMigrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ListMigrationsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *MigrationsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *MigrationsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ListMigrationsResponse) GetJSON200() *MigrationsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r ListMigrationsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8277,10 +10183,29 @@ func (r ListMigrationsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListMigrationsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type MigrateLegacyClinicianPatientsResponse struct { Body []byte HTTPResponse *http.Response - JSON202 *MigrationV1 + // JSON202 the response for an HTTP 202 `application/json` response + JSON202 *MigrationV1 +} + +// GetJSON202 returns the response for an HTTP 202 `application/json` response +func (r MigrateLegacyClinicianPatientsResponse) GetJSON202() *MigrationV1 { + return r.JSON202 +} + +// GetBody returns the raw response body bytes +func (r MigrateLegacyClinicianPatientsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8299,10 +10224,29 @@ func (r MigrateLegacyClinicianPatientsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r MigrateLegacyClinicianPatientsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetMigrationResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *MigrationV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *MigrationV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetMigrationResponse) GetJSON200() *MigrationV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetMigrationResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8321,10 +10265,29 @@ func (r GetMigrationResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetMigrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateMigrationResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *MigrationV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *MigrationV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpdateMigrationResponse) GetJSON200() *MigrationV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r UpdateMigrationResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8343,10 +10306,29 @@ func (r UpdateMigrationResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateMigrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetPatientCountResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientCountV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientCountV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPatientCountResponse) GetJSON200() *PatientCountV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetPatientCountResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8365,11 +10347,24 @@ func (r GetPatientCountResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPatientCountResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type RefreshPatientCountResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r RefreshPatientCountResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r RefreshPatientCountResponse) Status() string { if r.HTTPResponse != nil { @@ -8386,10 +10381,29 @@ func (r RefreshPatientCountResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r RefreshPatientCountResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type CreatePatientTagResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientTagV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientTagV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r CreatePatientTagResponse) GetJSON200() *PatientTagV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r CreatePatientTagResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8408,11 +10422,24 @@ func (r CreatePatientTagResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreatePatientTagResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeletePatientTagResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeletePatientTagResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeletePatientTagResponse) Status() string { if r.HTTPResponse != nil { @@ -8429,10 +10456,29 @@ func (r DeletePatientTagResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePatientTagResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdatePatientTagResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientTagV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientTagV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpdatePatientTagResponse) GetJSON200() *PatientTagV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r UpdatePatientTagResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8451,10 +10497,29 @@ func (r UpdatePatientTagResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdatePatientTagResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ConvertPatientTagToSiteResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *SiteV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *SiteV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ConvertPatientTagToSiteResponse) GetJSON200() *SiteV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r ConvertPatientTagToSiteResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8473,10 +10538,29 @@ func (r ConvertPatientTagToSiteResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ConvertPatientTagToSiteResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ListPatientsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientsResponseV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientsResponseV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ListPatientsResponse) GetJSON200() *PatientsResponseV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r ListPatientsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8495,10 +10579,29 @@ func (r ListPatientsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListPatientsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type CreatePatientAccountResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r CreatePatientAccountResponse) GetJSON200() *PatientV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r CreatePatientAccountResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8517,11 +10620,24 @@ func (r CreatePatientAccountResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreatePatientAccountResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type AssignPatientTagToClinicPatientsResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r AssignPatientTagToClinicPatientsResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r AssignPatientTagToClinicPatientsResponse) Status() string { if r.HTTPResponse != nil { @@ -8538,11 +10654,24 @@ func (r AssignPatientTagToClinicPatientsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r AssignPatientTagToClinicPatientsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeletePatientTagFromClinicPatientsResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeletePatientTagFromClinicPatientsResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeletePatientTagFromClinicPatientsResponse) Status() string { if r.HTTPResponse != nil { @@ -8559,11 +10688,24 @@ func (r DeletePatientTagFromClinicPatientsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePatientTagFromClinicPatientsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeletePatientResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeletePatientResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeletePatientResponse) Status() string { if r.HTTPResponse != nil { @@ -8580,10 +10722,29 @@ func (r DeletePatientResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePatientResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetPatientResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPatientResponse) GetJSON200() *PatientV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetPatientResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8602,10 +10763,29 @@ func (r GetPatientResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPatientResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type CreatePatientFromUserResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r CreatePatientFromUserResponse) GetJSON200() *PatientV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r CreatePatientFromUserResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8624,10 +10804,29 @@ func (r CreatePatientFromUserResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreatePatientFromUserResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdatePatientResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpdatePatientResponse) GetJSON200() *PatientV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r UpdatePatientResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8646,11 +10845,24 @@ func (r UpdatePatientResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdatePatientResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ConnectProviderResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r ConnectProviderResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r ConnectProviderResponse) Status() string { if r.HTTPResponse != nil { @@ -8667,10 +10879,29 @@ func (r ConnectProviderResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ConnectProviderResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdatePatientPermissionsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientPermissionsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientPermissionsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpdatePatientPermissionsResponse) GetJSON200() *PatientPermissionsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r UpdatePatientPermissionsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8689,11 +10920,24 @@ func (r UpdatePatientPermissionsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdatePatientPermissionsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeletePatientPermissionResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeletePatientPermissionResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeletePatientPermissionResponse) Status() string { if r.HTTPResponse != nil { @@ -8710,10 +10954,29 @@ func (r DeletePatientPermissionResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePatientPermissionResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeletePatientReviewsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientReviewsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientReviewsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r DeletePatientReviewsResponse) GetJSON200() *PatientReviewsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r DeletePatientReviewsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8732,10 +10995,29 @@ func (r DeletePatientReviewsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePatientReviewsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdatePatientReviewsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientReviewsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientReviewsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpdatePatientReviewsResponse) GetJSON200() *PatientReviewsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r UpdatePatientReviewsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8754,11 +11036,24 @@ func (r UpdatePatientReviewsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdatePatientReviewsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type SendUploadReminderResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r SendUploadReminderResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r SendUploadReminderResponse) Status() string { if r.HTTPResponse != nil { @@ -8775,11 +11070,24 @@ func (r SendUploadReminderResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SendUploadReminderResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GenerateMergeReportResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r GenerateMergeReportResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r GenerateMergeReportResponse) Status() string { if r.HTTPResponse != nil { @@ -8796,11 +11104,24 @@ func (r GenerateMergeReportResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GenerateMergeReportResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type AddServiceAccountResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r AddServiceAccountResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r AddServiceAccountResponse) Status() string { if r.HTTPResponse != nil { @@ -8817,10 +11138,29 @@ func (r AddServiceAccountResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r AddServiceAccountResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetEHRSettingsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *EhrSettingsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *EhrSettingsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetEHRSettingsResponse) GetJSON200() *EhrSettingsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetEHRSettingsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8839,11 +11179,24 @@ func (r GetEHRSettingsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEHRSettingsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateEHRSettingsResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r UpdateEHRSettingsResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r UpdateEHRSettingsResponse) Status() string { if r.HTTPResponse != nil { @@ -8860,10 +11213,29 @@ func (r UpdateEHRSettingsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateEHRSettingsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetMRNSettingsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *MrnSettingsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *MrnSettingsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetMRNSettingsResponse) GetJSON200() *MrnSettingsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetMRNSettingsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8882,11 +11254,24 @@ func (r GetMRNSettingsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetMRNSettingsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateMRNSettingsResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r UpdateMRNSettingsResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r UpdateMRNSettingsResponse) Status() string { if r.HTTPResponse != nil { @@ -8903,10 +11288,29 @@ func (r UpdateMRNSettingsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateMRNSettingsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type GetPatientCountSettingsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientCountSettingsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientCountSettingsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPatientCountSettingsResponse) GetJSON200() *PatientCountSettingsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r GetPatientCountSettingsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8925,11 +11329,24 @@ func (r GetPatientCountSettingsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPatientCountSettingsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdatePatientCountSettingsResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r UpdatePatientCountSettingsResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r UpdatePatientCountSettingsResponse) Status() string { if r.HTTPResponse != nil { @@ -8946,10 +11363,29 @@ func (r UpdatePatientCountSettingsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdatePatientCountSettingsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type CreateSiteResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *SiteV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *SiteV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r CreateSiteResponse) GetJSON200() *SiteV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r CreateSiteResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -8968,11 +11404,24 @@ func (r CreateSiteResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreateSiteResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeleteSiteResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeleteSiteResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeleteSiteResponse) Status() string { if r.HTTPResponse != nil { @@ -8989,10 +11438,29 @@ func (r DeleteSiteResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteSiteResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateSiteResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *SiteV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *SiteV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpdateSiteResponse) GetJSON200() *SiteV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r UpdateSiteResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -9011,10 +11479,29 @@ func (r UpdateSiteResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateSiteResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type MergeSiteResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *SiteV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *SiteV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r MergeSiteResponse) GetJSON200() *SiteV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r MergeSiteResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -9033,11 +11520,24 @@ func (r MergeSiteResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r MergeSiteResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateSuppressedNotificationsResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r UpdateSuppressedNotificationsResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r UpdateSuppressedNotificationsResponse) Status() string { if r.HTTPResponse != nil { @@ -9054,10 +11554,29 @@ func (r UpdateSuppressedNotificationsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateSuppressedNotificationsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type TideReportResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *TideResponseV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TideResponseV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r TideReportResponse) GetJSON200() *TideResponseV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r TideReportResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -9076,11 +11595,24 @@ func (r TideReportResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r TideReportResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateTierResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r UpdateTierResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r UpdateTierResponse) Status() string { if r.HTTPResponse != nil { @@ -9097,10 +11629,29 @@ func (r UpdateTierResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateTierResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type FindPatientsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientClinicRelationshipsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientClinicRelationshipsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r FindPatientsResponse) GetJSON200() *PatientClinicRelationshipsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r FindPatientsResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -9119,11 +11670,24 @@ func (r FindPatientsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r FindPatientsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type SyncEHRDataForPatientResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r SyncEHRDataForPatientResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r SyncEHRDataForPatientResponse) Status() string { if r.HTTPResponse != nil { @@ -9140,11 +11704,24 @@ func (r SyncEHRDataForPatientResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SyncEHRDataForPatientResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdatePatientSummaryResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r UpdatePatientSummaryResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r UpdatePatientSummaryResponse) Status() string { if r.HTTPResponse != nil { @@ -9161,10 +11738,29 @@ func (r UpdatePatientSummaryResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdatePatientSummaryResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ListClinicsForPatientResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *PatientClinicRelationshipsV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PatientClinicRelationshipsV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ListClinicsForPatientResponse) GetJSON200() *PatientClinicRelationshipsV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r ListClinicsForPatientResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -9183,11 +11779,24 @@ func (r ListClinicsForPatientResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ListClinicsForPatientResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdatePatientDataSourcesResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r UpdatePatientDataSourcesResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r UpdatePatientDataSourcesResponse) Status() string { if r.HTTPResponse != nil { @@ -9204,11 +11813,24 @@ func (r UpdatePatientDataSourcesResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdatePatientDataSourcesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ProcessEHRMessageResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r ProcessEHRMessageResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r ProcessEHRMessageResponse) Status() string { if r.HTTPResponse != nil { @@ -9225,10 +11847,29 @@ func (r ProcessEHRMessageResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ProcessEHRMessageResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type MatchClinicAndPatientResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *EhrMatchResponseV1 + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *EhrMatchResponseV1 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r MatchClinicAndPatientResponse) GetJSON200() *EhrMatchResponseV1 { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r MatchClinicAndPatientResponse) GetBody() []byte { + return r.Body } // Status returns HTTPResponse.Status @@ -9247,11 +11888,24 @@ func (r MatchClinicAndPatientResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r MatchClinicAndPatientResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type VerifyEndpointResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r VerifyEndpointResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r VerifyEndpointResponse) Status() string { if r.HTTPResponse != nil { @@ -9268,11 +11922,24 @@ func (r VerifyEndpointResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r VerifyEndpointResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeletePatientSummaryResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeletePatientSummaryResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeletePatientSummaryResponse) Status() string { if r.HTTPResponse != nil { @@ -9289,11 +11956,24 @@ func (r DeletePatientSummaryResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePatientSummaryResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type DeleteUserFromClinicsResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r DeleteUserFromClinicsResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r DeleteUserFromClinicsResponse) Status() string { if r.HTTPResponse != nil { @@ -9310,11 +11990,24 @@ func (r DeleteUserFromClinicsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteUserFromClinicsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type UpdateClinicUserDetailsResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r UpdateClinicUserDetailsResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r UpdateClinicUserDetailsResponse) Status() string { if r.HTTPResponse != nil { @@ -9331,11 +12024,24 @@ func (r UpdateClinicUserDetailsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateClinicUserDetailsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type XealthNotificationResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r XealthNotificationResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r XealthNotificationResponse) Status() string { if r.HTTPResponse != nil { @@ -9352,11 +12058,24 @@ func (r XealthNotificationResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r XealthNotificationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type XealthPreorderResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r XealthPreorderResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r XealthPreorderResponse) Status() string { if r.HTTPResponse != nil { @@ -9373,11 +12092,24 @@ func (r XealthPreorderResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r XealthPreorderResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type XealthGetProgramUrlResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r XealthGetProgramUrlResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r XealthGetProgramUrlResponse) Status() string { if r.HTTPResponse != nil { @@ -9394,11 +12126,24 @@ func (r XealthGetProgramUrlResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r XealthGetProgramUrlResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type XealthGetProgramsResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r XealthGetProgramsResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r XealthGetProgramsResponse) Status() string { if r.HTTPResponse != nil { @@ -9415,11 +12160,24 @@ func (r XealthGetProgramsResponse) StatusCode() int { return 0 } +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r XealthGetProgramsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type ViewPDFReportResponse struct { Body []byte HTTPResponse *http.Response } +// GetBody returns the raw response body bytes +func (r ViewPDFReportResponse) GetBody() []byte { + return r.Body +} + // Status returns HTTPResponse.Status func (r ViewPDFReportResponse) Status() string { if r.HTTPResponse != nil { @@ -9436,7 +12194,21 @@ func (r ViewPDFReportResponse) StatusCode() int { return 0 } -// ListAllCliniciansWithResponse request returning *ListAllCliniciansResponse +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ViewPDFReportResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +// ListAllCliniciansWithResponse List All Clinicians +// +// Returns list of all clinician/clinic relationships in the system. Internal endpoint used for reporting purposes. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinicians (the `ListAllClinicians` operationId). func (c *ClientWithResponses) ListAllCliniciansWithResponse(ctx context.Context, params *ListAllCliniciansParams, reqEditors ...RequestEditorFn) (*ListAllCliniciansResponse, error) { rsp, err := c.ListAllClinicians(ctx, params, reqEditors...) if err != nil { @@ -9445,7 +12217,13 @@ func (c *ClientWithResponses) ListAllCliniciansWithResponse(ctx context.Context, return ParseListAllCliniciansResponse(rsp) } -// ListClinicsForClinicianWithResponse request returning *ListClinicsForClinicianResponse +// ListClinicsForClinicianWithResponse List Clinics for Clinician +// +// Returns a list of all clinics a clinician is a member of. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinicians/{userId}/clinics (the `ListClinicsForClinician` operationId). func (c *ClientWithResponses) ListClinicsForClinicianWithResponse(ctx context.Context, userId UserId, params *ListClinicsForClinicianParams, reqEditors ...RequestEditorFn) (*ListClinicsForClinicianResponse, error) { rsp, err := c.ListClinicsForClinician(ctx, userId, params, reqEditors...) if err != nil { @@ -9454,7 +12232,13 @@ func (c *ClientWithResponses) ListClinicsForClinicianWithResponse(ctx context.Co return ParseListClinicsForClinicianResponse(rsp) } -// EnableNewClinicExperienceWithResponse request returning *EnableNewClinicExperienceResponse +// EnableNewClinicExperienceWithResponse Enable Clinics +// +// Internal only endpoint to enable new clinic experience for a legacy clinician account. Patients won't be migrated until the clinic details are populated and migration is explicitly kicked-off by blip. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinicians/{userId}/migrate (the `EnableNewClinicExperience` operationId). func (c *ClientWithResponses) EnableNewClinicExperienceWithResponse(ctx context.Context, userId string, reqEditors ...RequestEditorFn) (*EnableNewClinicExperienceResponse, error) { rsp, err := c.EnableNewClinicExperience(ctx, userId, reqEditors...) if err != nil { @@ -9463,7 +12247,13 @@ func (c *ClientWithResponses) EnableNewClinicExperienceWithResponse(ctx context. return ParseEnableNewClinicExperienceResponse(rsp) } -// ListClinicsWithResponse request returning *ListClinicsResponse +// ListClinicsWithResponse List Clinics +// +// Retrieve the list of clinics. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics (the `ListClinics` operationId). func (c *ClientWithResponses) ListClinicsWithResponse(ctx context.Context, params *ListClinicsParams, reqEditors ...RequestEditorFn) (*ListClinicsResponse, error) { rsp, err := c.ListClinics(ctx, params, reqEditors...) if err != nil { @@ -9472,7 +12262,13 @@ func (c *ClientWithResponses) ListClinicsWithResponse(ctx context.Context, param return ParseListClinicsResponse(rsp) } -// CreateClinicWithBodyWithResponse request with arbitrary body returning *CreateClinicResponse +// CreateClinicWithBodyWithResponse Create Clinic +// +// Create a new clinic. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics (the `CreateClinic` operationId). func (c *ClientWithResponses) CreateClinicWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateClinicResponse, error) { rsp, err := c.CreateClinicWithBody(ctx, contentType, body, reqEditors...) if err != nil { @@ -9481,6 +12277,13 @@ func (c *ClientWithResponses) CreateClinicWithBodyWithResponse(ctx context.Conte return ParseCreateClinicResponse(rsp) } +// CreateClinicWithResponse Create Clinic +// +// Create a new clinic. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics (the `CreateClinic` operationId). func (c *ClientWithResponses) CreateClinicWithResponse(ctx context.Context, body CreateClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateClinicResponse, error) { rsp, err := c.CreateClinic(ctx, body, reqEditors...) if err != nil { @@ -9489,7 +12292,13 @@ func (c *ClientWithResponses) CreateClinicWithResponse(ctx context.Context, body return ParseCreateClinicResponse(rsp) } -// GetClinicByShareCodeWithResponse request returning *GetClinicByShareCodeResponse +// GetClinicByShareCodeWithResponse Get Clinic by Share Code +// +// Retrieve a clinic object with a share code. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/share_code/{shareCode} (the `GetClinicByShareCode` operationId). func (c *ClientWithResponses) GetClinicByShareCodeWithResponse(ctx context.Context, shareCode string, reqEditors ...RequestEditorFn) (*GetClinicByShareCodeResponse, error) { rsp, err := c.GetClinicByShareCode(ctx, shareCode, reqEditors...) if err != nil { @@ -9498,7 +12307,13 @@ func (c *ClientWithResponses) GetClinicByShareCodeWithResponse(ctx context.Conte return ParseGetClinicByShareCodeResponse(rsp) } -// DeleteClinicWithResponse request returning *DeleteClinicResponse +// DeleteClinicWithResponse Delete Clinic +// +// Deletes a clinic if there are no connected patient accounts (besides demo accounts). +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/clinics/{clinicId} (the `DeleteClinic` operationId). func (c *ClientWithResponses) DeleteClinicWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*DeleteClinicResponse, error) { rsp, err := c.DeleteClinic(ctx, clinicId, reqEditors...) if err != nil { @@ -9507,7 +12322,13 @@ func (c *ClientWithResponses) DeleteClinicWithResponse(ctx context.Context, clin return ParseDeleteClinicResponse(rsp) } -// GetClinicWithResponse request returning *GetClinicResponse +// GetClinicWithResponse Get Clinic +// +// Retrieve a clinic by id. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId} (the `GetClinic` operationId). func (c *ClientWithResponses) GetClinicWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetClinicResponse, error) { rsp, err := c.GetClinic(ctx, clinicId, reqEditors...) if err != nil { @@ -9516,7 +12337,13 @@ func (c *ClientWithResponses) GetClinicWithResponse(ctx context.Context, clinicI return ParseGetClinicResponse(rsp) } -// UpdateClinicWithBodyWithResponse request with arbitrary body returning *UpdateClinicResponse +// UpdateClinicWithBodyWithResponse Update Clinic +// +// Update an existing clinic. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId} (the `UpdateClinic` operationId). func (c *ClientWithResponses) UpdateClinicWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateClinicResponse, error) { rsp, err := c.UpdateClinicWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -9525,6 +12352,13 @@ func (c *ClientWithResponses) UpdateClinicWithBodyWithResponse(ctx context.Conte return ParseUpdateClinicResponse(rsp) } +// UpdateClinicWithResponse Update Clinic +// +// Update an existing clinic. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId} (the `UpdateClinic` operationId). func (c *ClientWithResponses) UpdateClinicWithResponse(ctx context.Context, clinicId ClinicId, body UpdateClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateClinicResponse, error) { rsp, err := c.UpdateClinic(ctx, clinicId, body, reqEditors...) if err != nil { @@ -9533,7 +12367,13 @@ func (c *ClientWithResponses) UpdateClinicWithResponse(ctx context.Context, clin return ParseUpdateClinicResponse(rsp) } -// ListCliniciansWithResponse request returning *ListCliniciansResponse +// ListCliniciansWithResponse List Clinicians +// +// Retrieve the list of clinic members. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/clinicians (the `ListClinicians` operationId). func (c *ClientWithResponses) ListCliniciansWithResponse(ctx context.Context, clinicId ClinicId, params *ListCliniciansParams, reqEditors ...RequestEditorFn) (*ListCliniciansResponse, error) { rsp, err := c.ListClinicians(ctx, clinicId, params, reqEditors...) if err != nil { @@ -9542,7 +12382,13 @@ func (c *ClientWithResponses) ListCliniciansWithResponse(ctx context.Context, cl return ParseListCliniciansResponse(rsp) } -// CreateClinicianWithBodyWithResponse request with arbitrary body returning *CreateClinicianResponse +// CreateClinicianWithBodyWithResponse Create Clinician +// +// Internal endpoint to create a new clinician. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/clinicians (the `CreateClinician` operationId). func (c *ClientWithResponses) CreateClinicianWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateClinicianResponse, error) { rsp, err := c.CreateClinicianWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -9551,6 +12397,13 @@ func (c *ClientWithResponses) CreateClinicianWithBodyWithResponse(ctx context.Co return ParseCreateClinicianResponse(rsp) } +// CreateClinicianWithResponse Create Clinician +// +// Internal endpoint to create a new clinician. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/clinicians (the `CreateClinician` operationId). func (c *ClientWithResponses) CreateClinicianWithResponse(ctx context.Context, clinicId ClinicId, body CreateClinicianJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateClinicianResponse, error) { rsp, err := c.CreateClinician(ctx, clinicId, body, reqEditors...) if err != nil { @@ -9559,7 +12412,13 @@ func (c *ClientWithResponses) CreateClinicianWithResponse(ctx context.Context, c return ParseCreateClinicianResponse(rsp) } -// DeleteClinicianWithResponse request returning *DeleteClinicianResponse +// DeleteClinicianWithResponse Delete Clinician +// +// Removes a clinician from a clinic. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `DeleteClinician` operationId). func (c *ClientWithResponses) DeleteClinicianWithResponse(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, reqEditors ...RequestEditorFn) (*DeleteClinicianResponse, error) { rsp, err := c.DeleteClinician(ctx, clinicId, clinicianId, reqEditors...) if err != nil { @@ -9568,7 +12427,13 @@ func (c *ClientWithResponses) DeleteClinicianWithResponse(ctx context.Context, c return ParseDeleteClinicianResponse(rsp) } -// GetClinicianWithResponse request returning *GetClinicianResponse +// GetClinicianWithResponse Get Clinician +// +// Retrieve a member of clinic given their user id. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `GetClinician` operationId). func (c *ClientWithResponses) GetClinicianWithResponse(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, reqEditors ...RequestEditorFn) (*GetClinicianResponse, error) { rsp, err := c.GetClinician(ctx, clinicId, clinicianId, reqEditors...) if err != nil { @@ -9577,7 +12442,13 @@ func (c *ClientWithResponses) GetClinicianWithResponse(ctx context.Context, clin return ParseGetClinicianResponse(rsp) } -// UpdateClinicianWithBodyWithResponse request with arbitrary body returning *UpdateClinicianResponse +// UpdateClinicianWithBodyWithResponse Update Clinician +// +// Update existing clinician. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `UpdateClinician` operationId). func (c *ClientWithResponses) UpdateClinicianWithBodyWithResponse(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateClinicianResponse, error) { rsp, err := c.UpdateClinicianWithBody(ctx, clinicId, clinicianId, contentType, body, reqEditors...) if err != nil { @@ -9586,6 +12457,13 @@ func (c *ClientWithResponses) UpdateClinicianWithBodyWithResponse(ctx context.Co return ParseUpdateClinicianResponse(rsp) } +// UpdateClinicianWithResponse Update Clinician +// +// Update existing clinician. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/clinicians/{clinicianId} (the `UpdateClinician` operationId). func (c *ClientWithResponses) UpdateClinicianWithResponse(ctx context.Context, clinicId ClinicId, clinicianId ClinicianId, body UpdateClinicianJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateClinicianResponse, error) { rsp, err := c.UpdateClinician(ctx, clinicId, clinicianId, body, reqEditors...) if err != nil { @@ -9594,7 +12472,15 @@ func (c *ClientWithResponses) UpdateClinicianWithResponse(ctx context.Context, c return ParseUpdateClinicianResponse(rsp) } -// SyncEHRDataWithResponse request returning *SyncEHRDataResponse +// SyncEHRDataWithResponse Sync EHR Data +// +// An internal endpoint which will push the latest patient summary statistics and PDF reports for all patients who have an active subscription. The actual data is pushed asynchronously. A successful response means that an asynchronous task has been scheduled for each patient of the clinic with an active subscription. +// +// Will return 404 Not Found if the clinic does not have an active EHR connection. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/ehr/sync (the `SyncEHRData` operationId). func (c *ClientWithResponses) SyncEHRDataWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*SyncEHRDataResponse, error) { rsp, err := c.SyncEHRData(ctx, clinicId, reqEditors...) if err != nil { @@ -9603,7 +12489,13 @@ func (c *ClientWithResponses) SyncEHRDataWithResponse(ctx context.Context, clini return ParseSyncEHRDataResponse(rsp) } -// DeleteInvitedClinicianWithResponse request returning *DeleteInvitedClinicianResponse +// DeleteInvitedClinicianWithResponse Delete Invited Clinician +// +// Internal endpoint to delete an invited clinician object. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `DeleteInvitedClinician` operationId). func (c *ClientWithResponses) DeleteInvitedClinicianWithResponse(ctx context.Context, clinicId ClinicId, inviteId InviteId, reqEditors ...RequestEditorFn) (*DeleteInvitedClinicianResponse, error) { rsp, err := c.DeleteInvitedClinician(ctx, clinicId, inviteId, reqEditors...) if err != nil { @@ -9612,7 +12504,13 @@ func (c *ClientWithResponses) DeleteInvitedClinicianWithResponse(ctx context.Con return ParseDeleteInvitedClinicianResponse(rsp) } -// GetInvitedClinicianWithResponse request returning *GetInvitedClinicianResponse +// GetInvitedClinicianWithResponse Get Invited Clinician +// +// Internal endpoint to retrieve invited clinician. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `GetInvitedClinician` operationId). func (c *ClientWithResponses) GetInvitedClinicianWithResponse(ctx context.Context, clinicId ClinicId, inviteId InviteId, reqEditors ...RequestEditorFn) (*GetInvitedClinicianResponse, error) { rsp, err := c.GetInvitedClinician(ctx, clinicId, inviteId, reqEditors...) if err != nil { @@ -9621,7 +12519,13 @@ func (c *ClientWithResponses) GetInvitedClinicianWithResponse(ctx context.Contex return ParseGetInvitedClinicianResponse(rsp) } -// AssociateClinicianToUserWithBodyWithResponse request with arbitrary body returning *AssociateClinicianToUserResponse +// AssociateClinicianToUserWithBodyWithResponse Associate Clinician to User +// +// Internal endpoint to associate a clinician to a user. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PATCH /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `AssociateClinicianToUser` operationId). func (c *ClientWithResponses) AssociateClinicianToUserWithBodyWithResponse(ctx context.Context, clinicId ClinicId, inviteId InviteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AssociateClinicianToUserResponse, error) { rsp, err := c.AssociateClinicianToUserWithBody(ctx, clinicId, inviteId, contentType, body, reqEditors...) if err != nil { @@ -9630,6 +12534,13 @@ func (c *ClientWithResponses) AssociateClinicianToUserWithBodyWithResponse(ctx c return ParseAssociateClinicianToUserResponse(rsp) } +// AssociateClinicianToUserWithResponse Associate Clinician to User +// +// Internal endpoint to associate a clinician to a user. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PATCH /v1/clinics/{clinicId}/invites/clinicians/{inviteId}/clinician (the `AssociateClinicianToUser` operationId). func (c *ClientWithResponses) AssociateClinicianToUserWithResponse(ctx context.Context, clinicId ClinicId, inviteId InviteId, body AssociateClinicianToUserJSONRequestBody, reqEditors ...RequestEditorFn) (*AssociateClinicianToUserResponse, error) { rsp, err := c.AssociateClinicianToUser(ctx, clinicId, inviteId, body, reqEditors...) if err != nil { @@ -9638,7 +12549,15 @@ func (c *ClientWithResponses) AssociateClinicianToUserWithResponse(ctx context.C return ParseAssociateClinicianToUserResponse(rsp) } -// ListMembershipRestrictionsWithResponse request returning *ListMembershipRestrictionsResponse +// ListMembershipRestrictionsWithResponse List Membership Restrictions +// +// Returns a list of the membership restrictions that will be evaluated when a user joins a clinic. +// +// Only clinic admins can access this endpoint. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/membership_restrictions (the `ListMembershipRestrictions` operationId). func (c *ClientWithResponses) ListMembershipRestrictionsWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*ListMembershipRestrictionsResponse, error) { rsp, err := c.ListMembershipRestrictions(ctx, clinicId, reqEditors...) if err != nil { @@ -9647,7 +12566,13 @@ func (c *ClientWithResponses) ListMembershipRestrictionsWithResponse(ctx context return ParseListMembershipRestrictionsResponse(rsp) } -// UpdateMembershipRestrictionsWithBodyWithResponse request with arbitrary body returning *UpdateMembershipRestrictionsResponse +// UpdateMembershipRestrictionsWithBodyWithResponse Update Membership Restrictions +// +// An internal endpoint to update clinic membership restrictions. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/membership_restrictions (the `UpdateMembershipRestrictions` operationId). func (c *ClientWithResponses) UpdateMembershipRestrictionsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateMembershipRestrictionsResponse, error) { rsp, err := c.UpdateMembershipRestrictionsWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -9656,6 +12581,13 @@ func (c *ClientWithResponses) UpdateMembershipRestrictionsWithBodyWithResponse(c return ParseUpdateMembershipRestrictionsResponse(rsp) } +// UpdateMembershipRestrictionsWithResponse Update Membership Restrictions +// +// An internal endpoint to update clinic membership restrictions. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/membership_restrictions (the `UpdateMembershipRestrictions` operationId). func (c *ClientWithResponses) UpdateMembershipRestrictionsWithResponse(ctx context.Context, clinicId ClinicId, body UpdateMembershipRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateMembershipRestrictionsResponse, error) { rsp, err := c.UpdateMembershipRestrictions(ctx, clinicId, body, reqEditors...) if err != nil { @@ -9664,7 +12596,13 @@ func (c *ClientWithResponses) UpdateMembershipRestrictionsWithResponse(ctx conte return ParseUpdateMembershipRestrictionsResponse(rsp) } -// MergeClinicWithBodyWithResponse request with arbitrary body returning *MergeClinicResponse +// MergeClinicWithBodyWithResponse Merge Clinic +// +// # Merges tags, patients, clinicians, invites and share codes of the source clinic +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/merge (the `MergeClinic` operationId). func (c *ClientWithResponses) MergeClinicWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MergeClinicResponse, error) { rsp, err := c.MergeClinicWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -9673,6 +12611,13 @@ func (c *ClientWithResponses) MergeClinicWithBodyWithResponse(ctx context.Contex return ParseMergeClinicResponse(rsp) } +// MergeClinicWithResponse Merge Clinic +// +// # Merges tags, patients, clinicians, invites and share codes of the source clinic +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/merge (the `MergeClinic` operationId). func (c *ClientWithResponses) MergeClinicWithResponse(ctx context.Context, clinicId ClinicId, body MergeClinicJSONRequestBody, reqEditors ...RequestEditorFn) (*MergeClinicResponse, error) { rsp, err := c.MergeClinic(ctx, clinicId, body, reqEditors...) if err != nil { @@ -9681,7 +12626,13 @@ func (c *ClientWithResponses) MergeClinicWithResponse(ctx context.Context, clini return ParseMergeClinicResponse(rsp) } -// TriggerInitialMigrationWithBodyWithResponse request with arbitrary body returning *TriggerInitialMigrationResponse +// TriggerInitialMigrationWithBodyWithResponse Trigger initial migration +// +// Trigger the initial migration. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/migrate (the `TriggerInitialMigration` operationId). func (c *ClientWithResponses) TriggerInitialMigrationWithBodyWithResponse(ctx context.Context, clinicId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*TriggerInitialMigrationResponse, error) { rsp, err := c.TriggerInitialMigrationWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -9690,6 +12641,13 @@ func (c *ClientWithResponses) TriggerInitialMigrationWithBodyWithResponse(ctx co return ParseTriggerInitialMigrationResponse(rsp) } +// TriggerInitialMigrationWithResponse Trigger initial migration +// +// Trigger the initial migration. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/migrate (the `TriggerInitialMigration` operationId). func (c *ClientWithResponses) TriggerInitialMigrationWithResponse(ctx context.Context, clinicId string, body TriggerInitialMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*TriggerInitialMigrationResponse, error) { rsp, err := c.TriggerInitialMigration(ctx, clinicId, body, reqEditors...) if err != nil { @@ -9698,7 +12656,13 @@ func (c *ClientWithResponses) TriggerInitialMigrationWithResponse(ctx context.Co return ParseTriggerInitialMigrationResponse(rsp) } -// ListMigrationsWithResponse request returning *ListMigrationsResponse +// ListMigrationsWithResponse Retrieve Migration Status +// +// ListMigrations. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/migrations (the `ListMigrations` operationId). func (c *ClientWithResponses) ListMigrationsWithResponse(ctx context.Context, clinicId string, reqEditors ...RequestEditorFn) (*ListMigrationsResponse, error) { rsp, err := c.ListMigrations(ctx, clinicId, reqEditors...) if err != nil { @@ -9707,7 +12671,13 @@ func (c *ClientWithResponses) ListMigrationsWithResponse(ctx context.Context, cl return ParseListMigrationsResponse(rsp) } -// MigrateLegacyClinicianPatientsWithBodyWithResponse request with arbitrary body returning *MigrateLegacyClinicianPatientsResponse +// MigrateLegacyClinicianPatientsWithBodyWithResponse Migrate Legacy Clinician Patients +// +// Migrate the patients of a legacy clinic account. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/migrations (the `MigrateLegacyClinicianPatients` operationId). func (c *ClientWithResponses) MigrateLegacyClinicianPatientsWithBodyWithResponse(ctx context.Context, clinicId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MigrateLegacyClinicianPatientsResponse, error) { rsp, err := c.MigrateLegacyClinicianPatientsWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -9716,6 +12686,13 @@ func (c *ClientWithResponses) MigrateLegacyClinicianPatientsWithBodyWithResponse return ParseMigrateLegacyClinicianPatientsResponse(rsp) } +// MigrateLegacyClinicianPatientsWithResponse Migrate Legacy Clinician Patients +// +// Migrate the patients of a legacy clinic account. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/migrations (the `MigrateLegacyClinicianPatients` operationId). func (c *ClientWithResponses) MigrateLegacyClinicianPatientsWithResponse(ctx context.Context, clinicId string, body MigrateLegacyClinicianPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*MigrateLegacyClinicianPatientsResponse, error) { rsp, err := c.MigrateLegacyClinicianPatients(ctx, clinicId, body, reqEditors...) if err != nil { @@ -9724,7 +12701,13 @@ func (c *ClientWithResponses) MigrateLegacyClinicianPatientsWithResponse(ctx con return ParseMigrateLegacyClinicianPatientsResponse(rsp) } -// GetMigrationWithResponse request returning *GetMigrationResponse +// GetMigrationWithResponse Get Migration +// +// Get a migration by user id. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/migrations/{userId} (the `GetMigration` operationId). func (c *ClientWithResponses) GetMigrationWithResponse(ctx context.Context, clinicId ClinicIdV1, userId UserId, reqEditors ...RequestEditorFn) (*GetMigrationResponse, error) { rsp, err := c.GetMigration(ctx, clinicId, userId, reqEditors...) if err != nil { @@ -9733,7 +12716,13 @@ func (c *ClientWithResponses) GetMigrationWithResponse(ctx context.Context, clin return ParseGetMigrationResponse(rsp) } -// UpdateMigrationWithBodyWithResponse request with arbitrary body returning *UpdateMigrationResponse +// UpdateMigrationWithBodyWithResponse Update Migration +// +// Internal endpoint to update the status of a migration. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PATCH /v1/clinics/{clinicId}/migrations/{userId} (the `UpdateMigration` operationId). func (c *ClientWithResponses) UpdateMigrationWithBodyWithResponse(ctx context.Context, clinicId ClinicIdV1, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateMigrationResponse, error) { rsp, err := c.UpdateMigrationWithBody(ctx, clinicId, userId, contentType, body, reqEditors...) if err != nil { @@ -9742,6 +12731,13 @@ func (c *ClientWithResponses) UpdateMigrationWithBodyWithResponse(ctx context.Co return ParseUpdateMigrationResponse(rsp) } +// UpdateMigrationWithResponse Update Migration +// +// Internal endpoint to update the status of a migration. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PATCH /v1/clinics/{clinicId}/migrations/{userId} (the `UpdateMigration` operationId). func (c *ClientWithResponses) UpdateMigrationWithResponse(ctx context.Context, clinicId ClinicIdV1, userId UserId, body UpdateMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateMigrationResponse, error) { rsp, err := c.UpdateMigration(ctx, clinicId, userId, body, reqEditors...) if err != nil { @@ -9750,7 +12746,11 @@ func (c *ClientWithResponses) UpdateMigrationWithResponse(ctx context.Context, c return ParseUpdateMigrationResponse(rsp) } -// GetPatientCountWithResponse request returning *GetPatientCountResponse +// GetPatientCountWithResponse Get Patient Count +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/patient_count (the `GetPatientCount` operationId). func (c *ClientWithResponses) GetPatientCountWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetPatientCountResponse, error) { rsp, err := c.GetPatientCount(ctx, clinicId, reqEditors...) if err != nil { @@ -9759,7 +12759,13 @@ func (c *ClientWithResponses) GetPatientCountWithResponse(ctx context.Context, c return ParseGetPatientCountResponse(rsp) } -// RefreshPatientCountWithResponse request returning *RefreshPatientCountResponse +// RefreshPatientCountWithResponse Refresh Patient Count +// +// An internal endpoint to refresh the patient count for the specified clinic. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patient_count/refresh (the `RefreshPatientCount` operationId). func (c *ClientWithResponses) RefreshPatientCountWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*RefreshPatientCountResponse, error) { rsp, err := c.RefreshPatientCount(ctx, clinicId, reqEditors...) if err != nil { @@ -9768,7 +12774,13 @@ func (c *ClientWithResponses) RefreshPatientCountWithResponse(ctx context.Contex return ParseRefreshPatientCountResponse(rsp) } -// CreatePatientTagWithBodyWithResponse request with arbitrary body returning *CreatePatientTagResponse +// CreatePatientTagWithBodyWithResponse Create Patient Tag +// +// Create a new patient tag. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patient_tags (the `CreatePatientTag` operationId). func (c *ClientWithResponses) CreatePatientTagWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreatePatientTagResponse, error) { rsp, err := c.CreatePatientTagWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -9777,6 +12789,13 @@ func (c *ClientWithResponses) CreatePatientTagWithBodyWithResponse(ctx context.C return ParseCreatePatientTagResponse(rsp) } +// CreatePatientTagWithResponse Create Patient Tag +// +// Create a new patient tag. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patient_tags (the `CreatePatientTag` operationId). func (c *ClientWithResponses) CreatePatientTagWithResponse(ctx context.Context, clinicId ClinicId, body CreatePatientTagJSONRequestBody, reqEditors ...RequestEditorFn) (*CreatePatientTagResponse, error) { rsp, err := c.CreatePatientTag(ctx, clinicId, body, reqEditors...) if err != nil { @@ -9785,7 +12804,13 @@ func (c *ClientWithResponses) CreatePatientTagWithResponse(ctx context.Context, return ParseCreatePatientTagResponse(rsp) } -// DeletePatientTagWithResponse request returning *DeletePatientTagResponse +// DeletePatientTagWithResponse Delete Patient Tag +// +// Removes a patient tag from a clinic and tagged patients. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `DeletePatientTag` operationId). func (c *ClientWithResponses) DeletePatientTagWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, reqEditors ...RequestEditorFn) (*DeletePatientTagResponse, error) { rsp, err := c.DeletePatientTag(ctx, clinicId, patientTagId, reqEditors...) if err != nil { @@ -9794,7 +12819,13 @@ func (c *ClientWithResponses) DeletePatientTagWithResponse(ctx context.Context, return ParseDeletePatientTagResponse(rsp) } -// UpdatePatientTagWithBodyWithResponse request with arbitrary body returning *UpdatePatientTagResponse +// UpdatePatientTagWithBodyWithResponse Update Patient Tag +// +// Update existing patient tag. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `UpdatePatientTag` operationId). func (c *ClientWithResponses) UpdatePatientTagWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientTagResponse, error) { rsp, err := c.UpdatePatientTagWithBody(ctx, clinicId, patientTagId, contentType, body, reqEditors...) if err != nil { @@ -9803,6 +12834,13 @@ func (c *ClientWithResponses) UpdatePatientTagWithBodyWithResponse(ctx context.C return ParseUpdatePatientTagResponse(rsp) } +// UpdatePatientTagWithResponse Update Patient Tag +// +// Update existing patient tag. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/patient_tags/{patientTagId} (the `UpdatePatientTag` operationId). func (c *ClientWithResponses) UpdatePatientTagWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body UpdatePatientTagJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientTagResponse, error) { rsp, err := c.UpdatePatientTag(ctx, clinicId, patientTagId, body, reqEditors...) if err != nil { @@ -9811,7 +12849,13 @@ func (c *ClientWithResponses) UpdatePatientTagWithResponse(ctx context.Context, return ParseUpdatePatientTagResponse(rsp) } -// ConvertPatientTagToSiteWithResponse request returning *ConvertPatientTagToSiteResponse +// ConvertPatientTagToSiteWithResponse Convert Patient Tag to Site +// +// Convert a patient tag to a site. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patient_tags/{patientTagId}/site (the `ConvertPatientTagToSite` operationId). func (c *ClientWithResponses) ConvertPatientTagToSiteWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, reqEditors ...RequestEditorFn) (*ConvertPatientTagToSiteResponse, error) { rsp, err := c.ConvertPatientTagToSite(ctx, clinicId, patientTagId, reqEditors...) if err != nil { @@ -9820,7 +12864,13 @@ func (c *ClientWithResponses) ConvertPatientTagToSiteWithResponse(ctx context.Co return ParseConvertPatientTagToSiteResponse(rsp) } -// ListPatientsWithResponse request returning *ListPatientsResponse +// ListPatientsWithResponse List Patients +// +// Retrieve a list of patients of a clinic. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/patients (the `ListPatients` operationId). func (c *ClientWithResponses) ListPatientsWithResponse(ctx context.Context, clinicId ClinicId, params *ListPatientsParams, reqEditors ...RequestEditorFn) (*ListPatientsResponse, error) { rsp, err := c.ListPatients(ctx, clinicId, params, reqEditors...) if err != nil { @@ -9829,7 +12879,13 @@ func (c *ClientWithResponses) ListPatientsWithResponse(ctx context.Context, clin return ParseListPatientsResponse(rsp) } -// CreatePatientAccountWithBodyWithResponse request with arbitrary body returning *CreatePatientAccountResponse +// CreatePatientAccountWithBodyWithResponse Create Patient Account +// +// Create a custodial account for a patient. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients (the `CreatePatientAccount` operationId). func (c *ClientWithResponses) CreatePatientAccountWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreatePatientAccountResponse, error) { rsp, err := c.CreatePatientAccountWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -9838,6 +12894,13 @@ func (c *ClientWithResponses) CreatePatientAccountWithBodyWithResponse(ctx conte return ParseCreatePatientAccountResponse(rsp) } +// CreatePatientAccountWithResponse Create Patient Account +// +// Create a custodial account for a patient. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients (the `CreatePatientAccount` operationId). func (c *ClientWithResponses) CreatePatientAccountWithResponse(ctx context.Context, clinicId ClinicId, body CreatePatientAccountJSONRequestBody, reqEditors ...RequestEditorFn) (*CreatePatientAccountResponse, error) { rsp, err := c.CreatePatientAccount(ctx, clinicId, body, reqEditors...) if err != nil { @@ -9846,7 +12909,13 @@ func (c *ClientWithResponses) CreatePatientAccountWithResponse(ctx context.Conte return ParseCreatePatientAccountResponse(rsp) } -// AssignPatientTagToClinicPatientsWithBodyWithResponse request with arbitrary body returning *AssignPatientTagToClinicPatientsResponse +// AssignPatientTagToClinicPatientsWithBodyWithResponse Assign Patient Tag To Clinic Patients +// +// Assign a patient tag to a subset of clinic patients. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/assign_tag/{patientTagId} (the `AssignPatientTagToClinicPatients` operationId). func (c *ClientWithResponses) AssignPatientTagToClinicPatientsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AssignPatientTagToClinicPatientsResponse, error) { rsp, err := c.AssignPatientTagToClinicPatientsWithBody(ctx, clinicId, patientTagId, contentType, body, reqEditors...) if err != nil { @@ -9855,6 +12924,13 @@ func (c *ClientWithResponses) AssignPatientTagToClinicPatientsWithBodyWithRespon return ParseAssignPatientTagToClinicPatientsResponse(rsp) } +// AssignPatientTagToClinicPatientsWithResponse Assign Patient Tag To Clinic Patients +// +// Assign a patient tag to a subset of clinic patients. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/assign_tag/{patientTagId} (the `AssignPatientTagToClinicPatients` operationId). func (c *ClientWithResponses) AssignPatientTagToClinicPatientsWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body AssignPatientTagToClinicPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*AssignPatientTagToClinicPatientsResponse, error) { rsp, err := c.AssignPatientTagToClinicPatients(ctx, clinicId, patientTagId, body, reqEditors...) if err != nil { @@ -9863,7 +12939,13 @@ func (c *ClientWithResponses) AssignPatientTagToClinicPatientsWithResponse(ctx c return ParseAssignPatientTagToClinicPatientsResponse(rsp) } -// DeletePatientTagFromClinicPatientsWithBodyWithResponse request with arbitrary body returning *DeletePatientTagFromClinicPatientsResponse +// DeletePatientTagFromClinicPatientsWithBodyWithResponse Delete Patient Tag From Clinic Patients +// +// Delete a patient tag from all or a subset of clinic patients. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/delete_tag/{patientTagId} (the `DeletePatientTagFromClinicPatients` operationId). func (c *ClientWithResponses) DeletePatientTagFromClinicPatientsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeletePatientTagFromClinicPatientsResponse, error) { rsp, err := c.DeletePatientTagFromClinicPatientsWithBody(ctx, clinicId, patientTagId, contentType, body, reqEditors...) if err != nil { @@ -9872,6 +12954,13 @@ func (c *ClientWithResponses) DeletePatientTagFromClinicPatientsWithBodyWithResp return ParseDeletePatientTagFromClinicPatientsResponse(rsp) } +// DeletePatientTagFromClinicPatientsWithResponse Delete Patient Tag From Clinic Patients +// +// Delete a patient tag from all or a subset of clinic patients. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/delete_tag/{patientTagId} (the `DeletePatientTagFromClinicPatients` operationId). func (c *ClientWithResponses) DeletePatientTagFromClinicPatientsWithResponse(ctx context.Context, clinicId ClinicId, patientTagId PatientTagId, body DeletePatientTagFromClinicPatientsJSONRequestBody, reqEditors ...RequestEditorFn) (*DeletePatientTagFromClinicPatientsResponse, error) { rsp, err := c.DeletePatientTagFromClinicPatients(ctx, clinicId, patientTagId, body, reqEditors...) if err != nil { @@ -9880,7 +12969,13 @@ func (c *ClientWithResponses) DeletePatientTagFromClinicPatientsWithResponse(ctx return ParseDeletePatientTagFromClinicPatientsResponse(rsp) } -// DeletePatientWithResponse request returning *DeletePatientResponse +// DeletePatientWithResponse Delete Patient +// +// Deletes patient from a clinic. Requires the request to be made by a clinic admin or the currently authenticated user id to match the patient id. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId} (the `DeletePatient` operationId). func (c *ClientWithResponses) DeletePatientWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*DeletePatientResponse, error) { rsp, err := c.DeletePatient(ctx, clinicId, patientId, reqEditors...) if err != nil { @@ -9889,7 +12984,13 @@ func (c *ClientWithResponses) DeletePatientWithResponse(ctx context.Context, cli return ParseDeletePatientResponse(rsp) } -// GetPatientWithResponse request returning *GetPatientResponse +// GetPatientWithResponse Get Patient +// +// Retrieve a patient of clinic given their user id. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/patients/{patientId} (the `GetPatient` operationId). func (c *ClientWithResponses) GetPatientWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*GetPatientResponse, error) { rsp, err := c.GetPatient(ctx, clinicId, patientId, reqEditors...) if err != nil { @@ -9898,7 +12999,13 @@ func (c *ClientWithResponses) GetPatientWithResponse(ctx context.Context, clinic return ParseGetPatientResponse(rsp) } -// CreatePatientFromUserWithBodyWithResponse request with arbitrary body returning *CreatePatientFromUserResponse +// CreatePatientFromUserWithBodyWithResponse Create Patient from Existing User +// +// Internal endpoint for creating a patient from an existing user. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId} (the `CreatePatientFromUser` operationId). func (c *ClientWithResponses) CreatePatientFromUserWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreatePatientFromUserResponse, error) { rsp, err := c.CreatePatientFromUserWithBody(ctx, clinicId, patientId, contentType, body, reqEditors...) if err != nil { @@ -9907,6 +13014,13 @@ func (c *ClientWithResponses) CreatePatientFromUserWithBodyWithResponse(ctx cont return ParseCreatePatientFromUserResponse(rsp) } +// CreatePatientFromUserWithResponse Create Patient from Existing User +// +// Internal endpoint for creating a patient from an existing user. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId} (the `CreatePatientFromUser` operationId). func (c *ClientWithResponses) CreatePatientFromUserWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, body CreatePatientFromUserJSONRequestBody, reqEditors ...RequestEditorFn) (*CreatePatientFromUserResponse, error) { rsp, err := c.CreatePatientFromUser(ctx, clinicId, patientId, body, reqEditors...) if err != nil { @@ -9915,7 +13029,13 @@ func (c *ClientWithResponses) CreatePatientFromUserWithResponse(ctx context.Cont return ParseCreatePatientFromUserResponse(rsp) } -// UpdatePatientWithBodyWithResponse request with arbitrary body returning *UpdatePatientResponse +// UpdatePatientWithBodyWithResponse Update Patient +// +// Update existing patient of a clinic. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId} (the `UpdatePatient` operationId). func (c *ClientWithResponses) UpdatePatientWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientResponse, error) { rsp, err := c.UpdatePatientWithBody(ctx, clinicId, patientId, contentType, body, reqEditors...) if err != nil { @@ -9924,6 +13044,13 @@ func (c *ClientWithResponses) UpdatePatientWithBodyWithResponse(ctx context.Cont return ParseUpdatePatientResponse(rsp) } +// UpdatePatientWithResponse Update Patient +// +// Update existing patient of a clinic. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId} (the `UpdatePatient` operationId). func (c *ClientWithResponses) UpdatePatientWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, body UpdatePatientJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientResponse, error) { rsp, err := c.UpdatePatient(ctx, clinicId, patientId, body, reqEditors...) if err != nil { @@ -9932,7 +13059,13 @@ func (c *ClientWithResponses) UpdatePatientWithResponse(ctx context.Context, cli return ParseUpdatePatientResponse(rsp) } -// ConnectProviderWithResponse request returning *ConnectProviderResponse +// ConnectProviderWithResponse Connect Provider +// +// Send a new request to the patient to connect a data provider. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId}/connect/{providerId} (the `ConnectProvider` operationId). func (c *ClientWithResponses) ConnectProviderWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, providerId ProviderId, reqEditors ...RequestEditorFn) (*ConnectProviderResponse, error) { rsp, err := c.ConnectProvider(ctx, clinicId, patientId, providerId, reqEditors...) if err != nil { @@ -9941,7 +13074,15 @@ func (c *ClientWithResponses) ConnectProviderWithResponse(ctx context.Context, c return ParseConnectProviderResponse(rsp) } -// UpdatePatientPermissionsWithBodyWithResponse request with arbitrary body returning *UpdatePatientPermissionsResponse +// UpdatePatientPermissionsWithBodyWithResponse Update Patient Permissions +// +// Update permissions that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. +// +// Only patients can change the permissions. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/permissions (the `UpdatePatientPermissions` operationId). func (c *ClientWithResponses) UpdatePatientPermissionsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientPermissionsResponse, error) { rsp, err := c.UpdatePatientPermissionsWithBody(ctx, clinicId, patientId, contentType, body, reqEditors...) if err != nil { @@ -9950,6 +13091,15 @@ func (c *ClientWithResponses) UpdatePatientPermissionsWithBodyWithResponse(ctx c return ParseUpdatePatientPermissionsResponse(rsp) } +// UpdatePatientPermissionsWithResponse Update Patient Permissions +// +// Update permissions that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. +// +// Only patients can change the permissions. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/permissions (the `UpdatePatientPermissions` operationId). func (c *ClientWithResponses) UpdatePatientPermissionsWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, body UpdatePatientPermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientPermissionsResponse, error) { rsp, err := c.UpdatePatientPermissions(ctx, clinicId, patientId, body, reqEditors...) if err != nil { @@ -9958,7 +13108,15 @@ func (c *ClientWithResponses) UpdatePatientPermissionsWithResponse(ctx context.C return ParseUpdatePatientPermissionsResponse(rsp) } -// DeletePatientPermissionWithResponse request returning *DeletePatientPermissionResponse +// DeletePatientPermissionWithResponse Delete Patient Permission +// +// Remove a single permission that a clinic has over a patient account. If all permissions are revoked as a result of the update, the patient profile will be removed from the clinic. +// +// Only patients can change the permissions. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId}/permissions/{permission} (the `DeletePatientPermission` operationId). func (c *ClientWithResponses) DeletePatientPermissionWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, permission string, reqEditors ...RequestEditorFn) (*DeletePatientPermissionResponse, error) { rsp, err := c.DeletePatientPermission(ctx, clinicId, patientId, permission, reqEditors...) if err != nil { @@ -9967,7 +13125,13 @@ func (c *ClientWithResponses) DeletePatientPermissionWithResponse(ctx context.Co return ParseDeletePatientPermissionResponse(rsp) } -// DeletePatientReviewsWithResponse request returning *DeletePatientReviewsResponse +// DeletePatientReviewsWithResponse Delete Patient Reviews +// +// Revert the patient's last reviewed date to the previous set. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/clinics/{clinicId}/patients/{patientId}/reviews (the `DeletePatientReviews` operationId). func (c *ClientWithResponses) DeletePatientReviewsWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*DeletePatientReviewsResponse, error) { rsp, err := c.DeletePatientReviews(ctx, clinicId, patientId, reqEditors...) if err != nil { @@ -9976,7 +13140,13 @@ func (c *ClientWithResponses) DeletePatientReviewsWithResponse(ctx context.Conte return ParseDeletePatientReviewsResponse(rsp) } -// UpdatePatientReviewsWithResponse request returning *UpdatePatientReviewsResponse +// UpdatePatientReviewsWithResponse Update Patient Reviews +// +// Update the patient's last reviewed date and clinician ID. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/patients/{patientId}/reviews (the `UpdatePatientReviews` operationId). func (c *ClientWithResponses) UpdatePatientReviewsWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*UpdatePatientReviewsResponse, error) { rsp, err := c.UpdatePatientReviews(ctx, clinicId, patientId, reqEditors...) if err != nil { @@ -9985,7 +13155,13 @@ func (c *ClientWithResponses) UpdatePatientReviewsWithResponse(ctx context.Conte return ParseUpdatePatientReviewsResponse(rsp) } -// SendUploadReminderWithResponse request returning *SendUploadReminderResponse +// SendUploadReminderWithResponse Send Upload Reminder +// +// Sends upload reminder to the patient. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/patients/{patientId}/upload_reminder (the `SendUploadReminder` operationId). func (c *ClientWithResponses) SendUploadReminderWithResponse(ctx context.Context, clinicId ClinicId, patientId PatientId, reqEditors ...RequestEditorFn) (*SendUploadReminderResponse, error) { rsp, err := c.SendUploadReminder(ctx, clinicId, patientId, reqEditors...) if err != nil { @@ -9994,7 +13170,13 @@ func (c *ClientWithResponses) SendUploadReminderWithResponse(ctx context.Context return ParseSendUploadReminderResponse(rsp) } -// GenerateMergeReportWithBodyWithResponse request with arbitrary body returning *GenerateMergeReportResponse +// GenerateMergeReportWithBodyWithResponse Generate Clinic Merge Report +// +// Generates a spreadsheet report for merging two clinics. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/reports/merge (the `GenerateMergeReport` operationId). func (c *ClientWithResponses) GenerateMergeReportWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GenerateMergeReportResponse, error) { rsp, err := c.GenerateMergeReportWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -10003,6 +13185,13 @@ func (c *ClientWithResponses) GenerateMergeReportWithBodyWithResponse(ctx contex return ParseGenerateMergeReportResponse(rsp) } +// GenerateMergeReportWithResponse Generate Clinic Merge Report +// +// Generates a spreadsheet report for merging two clinics. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/reports/merge (the `GenerateMergeReport` operationId). func (c *ClientWithResponses) GenerateMergeReportWithResponse(ctx context.Context, clinicId ClinicId, body GenerateMergeReportJSONRequestBody, reqEditors ...RequestEditorFn) (*GenerateMergeReportResponse, error) { rsp, err := c.GenerateMergeReport(ctx, clinicId, body, reqEditors...) if err != nil { @@ -10011,7 +13200,13 @@ func (c *ClientWithResponses) GenerateMergeReportWithResponse(ctx context.Contex return ParseGenerateMergeReportResponse(rsp) } -// AddServiceAccountWithBodyWithResponse request with arbitrary body returning *AddServiceAccountResponse +// AddServiceAccountWithBodyWithResponse Add Service Account +// +// Adds authentication client's service account as a new clinician member. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/service_accounts (the `AddServiceAccount` operationId). func (c *ClientWithResponses) AddServiceAccountWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AddServiceAccountResponse, error) { rsp, err := c.AddServiceAccountWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -10020,6 +13215,13 @@ func (c *ClientWithResponses) AddServiceAccountWithBodyWithResponse(ctx context. return ParseAddServiceAccountResponse(rsp) } +// AddServiceAccountWithResponse Add Service Account +// +// Adds authentication client's service account as a new clinician member. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/service_accounts (the `AddServiceAccount` operationId). func (c *ClientWithResponses) AddServiceAccountWithResponse(ctx context.Context, clinicId ClinicId, body AddServiceAccountJSONRequestBody, reqEditors ...RequestEditorFn) (*AddServiceAccountResponse, error) { rsp, err := c.AddServiceAccount(ctx, clinicId, body, reqEditors...) if err != nil { @@ -10028,7 +13230,13 @@ func (c *ClientWithResponses) AddServiceAccountWithResponse(ctx context.Context, return ParseAddServiceAccountResponse(rsp) } -// GetEHRSettingsWithResponse request returning *GetEHRSettingsResponse +// GetEHRSettingsWithResponse Get EHR Settings +// +// Returns EHR related settings. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/settings/ehr (the `GetEHRSettings` operationId). func (c *ClientWithResponses) GetEHRSettingsWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetEHRSettingsResponse, error) { rsp, err := c.GetEHRSettings(ctx, clinicId, reqEditors...) if err != nil { @@ -10037,7 +13245,13 @@ func (c *ClientWithResponses) GetEHRSettingsWithResponse(ctx context.Context, cl return ParseGetEHRSettingsResponse(rsp) } -// UpdateEHRSettingsWithBodyWithResponse request with arbitrary body returning *UpdateEHRSettingsResponse +// UpdateEHRSettingsWithBodyWithResponse Update EHR Settings +// +// Update EHR related settings for a clinic. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/ehr (the `UpdateEHRSettings` operationId). func (c *ClientWithResponses) UpdateEHRSettingsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateEHRSettingsResponse, error) { rsp, err := c.UpdateEHRSettingsWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -10046,6 +13260,13 @@ func (c *ClientWithResponses) UpdateEHRSettingsWithBodyWithResponse(ctx context. return ParseUpdateEHRSettingsResponse(rsp) } +// UpdateEHRSettingsWithResponse Update EHR Settings +// +// Update EHR related settings for a clinic. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/ehr (the `UpdateEHRSettings` operationId). func (c *ClientWithResponses) UpdateEHRSettingsWithResponse(ctx context.Context, clinicId ClinicId, body UpdateEHRSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateEHRSettingsResponse, error) { rsp, err := c.UpdateEHRSettings(ctx, clinicId, body, reqEditors...) if err != nil { @@ -10054,7 +13275,13 @@ func (c *ClientWithResponses) UpdateEHRSettingsWithResponse(ctx context.Context, return ParseUpdateEHRSettingsResponse(rsp) } -// GetMRNSettingsWithResponse request returning *GetMRNSettingsResponse +// GetMRNSettingsWithResponse Get MRN Settings +// +// Retrieve MRN settings. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/settings/mrn (the `GetMRNSettings` operationId). func (c *ClientWithResponses) GetMRNSettingsWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetMRNSettingsResponse, error) { rsp, err := c.GetMRNSettings(ctx, clinicId, reqEditors...) if err != nil { @@ -10063,7 +13290,11 @@ func (c *ClientWithResponses) GetMRNSettingsWithResponse(ctx context.Context, cl return ParseGetMRNSettingsResponse(rsp) } -// UpdateMRNSettingsWithBodyWithResponse request with arbitrary body returning *UpdateMRNSettingsResponse +// UpdateMRNSettingsWithBodyWithResponse Update MRN Settings +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/mrn (the `UpdateMRNSettings` operationId). func (c *ClientWithResponses) UpdateMRNSettingsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateMRNSettingsResponse, error) { rsp, err := c.UpdateMRNSettingsWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -10072,6 +13303,11 @@ func (c *ClientWithResponses) UpdateMRNSettingsWithBodyWithResponse(ctx context. return ParseUpdateMRNSettingsResponse(rsp) } +// UpdateMRNSettingsWithResponse Update MRN Settings +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/mrn (the `UpdateMRNSettings` operationId). func (c *ClientWithResponses) UpdateMRNSettingsWithResponse(ctx context.Context, clinicId ClinicId, body UpdateMRNSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateMRNSettingsResponse, error) { rsp, err := c.UpdateMRNSettings(ctx, clinicId, body, reqEditors...) if err != nil { @@ -10080,7 +13316,13 @@ func (c *ClientWithResponses) UpdateMRNSettingsWithResponse(ctx context.Context, return ParseUpdateMRNSettingsResponse(rsp) } -// GetPatientCountSettingsWithResponse request returning *GetPatientCountSettingsResponse +// GetPatientCountSettingsWithResponse Get Patient Count Settings +// +// Get Patient Count settings. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/settings/patient_count (the `GetPatientCountSettings` operationId). func (c *ClientWithResponses) GetPatientCountSettingsWithResponse(ctx context.Context, clinicId ClinicId, reqEditors ...RequestEditorFn) (*GetPatientCountSettingsResponse, error) { rsp, err := c.GetPatientCountSettings(ctx, clinicId, reqEditors...) if err != nil { @@ -10089,7 +13331,11 @@ func (c *ClientWithResponses) GetPatientCountSettingsWithResponse(ctx context.Co return ParseGetPatientCountSettingsResponse(rsp) } -// UpdatePatientCountSettingsWithBodyWithResponse request with arbitrary body returning *UpdatePatientCountSettingsResponse +// UpdatePatientCountSettingsWithBodyWithResponse Update Patient Count Settings +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/patient_count (the `UpdatePatientCountSettings` operationId). func (c *ClientWithResponses) UpdatePatientCountSettingsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientCountSettingsResponse, error) { rsp, err := c.UpdatePatientCountSettingsWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -10098,6 +13344,11 @@ func (c *ClientWithResponses) UpdatePatientCountSettingsWithBodyWithResponse(ctx return ParseUpdatePatientCountSettingsResponse(rsp) } +// UpdatePatientCountSettingsWithResponse Update Patient Count Settings +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/settings/patient_count (the `UpdatePatientCountSettings` operationId). func (c *ClientWithResponses) UpdatePatientCountSettingsWithResponse(ctx context.Context, clinicId ClinicId, body UpdatePatientCountSettingsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientCountSettingsResponse, error) { rsp, err := c.UpdatePatientCountSettings(ctx, clinicId, body, reqEditors...) if err != nil { @@ -10106,7 +13357,11 @@ func (c *ClientWithResponses) UpdatePatientCountSettingsWithResponse(ctx context return ParseUpdatePatientCountSettingsResponse(rsp) } -// CreateSiteWithBodyWithResponse request with arbitrary body returning *CreateSiteResponse +// CreateSiteWithBodyWithResponse Create a Site +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/sites (the `CreateSite` operationId). func (c *ClientWithResponses) CreateSiteWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateSiteResponse, error) { rsp, err := c.CreateSiteWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -10115,6 +13370,11 @@ func (c *ClientWithResponses) CreateSiteWithBodyWithResponse(ctx context.Context return ParseCreateSiteResponse(rsp) } +// CreateSiteWithResponse Create a Site +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/sites (the `CreateSite` operationId). func (c *ClientWithResponses) CreateSiteWithResponse(ctx context.Context, clinicId ClinicId, body CreateSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateSiteResponse, error) { rsp, err := c.CreateSite(ctx, clinicId, body, reqEditors...) if err != nil { @@ -10123,7 +13383,11 @@ func (c *ClientWithResponses) CreateSiteWithResponse(ctx context.Context, clinic return ParseCreateSiteResponse(rsp) } -// DeleteSiteWithResponse request returning *DeleteSiteResponse +// DeleteSiteWithResponse Delete a Site +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/clinics/{clinicId}/sites/{siteId} (the `DeleteSite` operationId). func (c *ClientWithResponses) DeleteSiteWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, reqEditors ...RequestEditorFn) (*DeleteSiteResponse, error) { rsp, err := c.DeleteSite(ctx, clinicId, siteId, reqEditors...) if err != nil { @@ -10132,7 +13396,11 @@ func (c *ClientWithResponses) DeleteSiteWithResponse(ctx context.Context, clinic return ParseDeleteSiteResponse(rsp) } -// UpdateSiteWithBodyWithResponse request with arbitrary body returning *UpdateSiteResponse +// UpdateSiteWithBodyWithResponse Update a Site +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/sites/{siteId} (the `UpdateSite` operationId). func (c *ClientWithResponses) UpdateSiteWithBodyWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateSiteResponse, error) { rsp, err := c.UpdateSiteWithBody(ctx, clinicId, siteId, contentType, body, reqEditors...) if err != nil { @@ -10141,6 +13409,11 @@ func (c *ClientWithResponses) UpdateSiteWithBodyWithResponse(ctx context.Context return ParseUpdateSiteResponse(rsp) } +// UpdateSiteWithResponse Update a Site +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/clinics/{clinicId}/sites/{siteId} (the `UpdateSite` operationId). func (c *ClientWithResponses) UpdateSiteWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, body UpdateSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateSiteResponse, error) { rsp, err := c.UpdateSite(ctx, clinicId, siteId, body, reqEditors...) if err != nil { @@ -10149,7 +13422,13 @@ func (c *ClientWithResponses) UpdateSiteWithResponse(ctx context.Context, clinic return ParseUpdateSiteResponse(rsp) } -// MergeSiteWithBodyWithResponse request with arbitrary body returning *MergeSiteResponse +// MergeSiteWithBodyWithResponse Merge two sites +// +// Merge a site, identified in the request body, into the site identified in the URL. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/sites/{siteId}/merge (the `MergeSite` operationId). func (c *ClientWithResponses) MergeSiteWithBodyWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MergeSiteResponse, error) { rsp, err := c.MergeSiteWithBody(ctx, clinicId, siteId, contentType, body, reqEditors...) if err != nil { @@ -10158,6 +13437,13 @@ func (c *ClientWithResponses) MergeSiteWithBodyWithResponse(ctx context.Context, return ParseMergeSiteResponse(rsp) } +// MergeSiteWithResponse Merge two sites +// +// Merge a site, identified in the request body, into the site identified in the URL. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/sites/{siteId}/merge (the `MergeSite` operationId). func (c *ClientWithResponses) MergeSiteWithResponse(ctx context.Context, clinicId ClinicId, siteId SiteId, body MergeSiteJSONRequestBody, reqEditors ...RequestEditorFn) (*MergeSiteResponse, error) { rsp, err := c.MergeSite(ctx, clinicId, siteId, body, reqEditors...) if err != nil { @@ -10166,7 +13452,13 @@ func (c *ClientWithResponses) MergeSiteWithResponse(ctx context.Context, clinicI return ParseMergeSiteResponse(rsp) } -// UpdateSuppressedNotificationsWithBodyWithResponse request with arbitrary body returning *UpdateSuppressedNotificationsResponse +// UpdateSuppressedNotificationsWithBodyWithResponse Update Suppressed Notifications +// +// Endpoint to update clinic suppressed notifications. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/suppressed_notifications (the `UpdateSuppressedNotifications` operationId). func (c *ClientWithResponses) UpdateSuppressedNotificationsWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateSuppressedNotificationsResponse, error) { rsp, err := c.UpdateSuppressedNotificationsWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -10175,6 +13467,13 @@ func (c *ClientWithResponses) UpdateSuppressedNotificationsWithBodyWithResponse( return ParseUpdateSuppressedNotificationsResponse(rsp) } +// UpdateSuppressedNotificationsWithResponse Update Suppressed Notifications +// +// Endpoint to update clinic suppressed notifications. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/suppressed_notifications (the `UpdateSuppressedNotifications` operationId). func (c *ClientWithResponses) UpdateSuppressedNotificationsWithResponse(ctx context.Context, clinicId ClinicId, body UpdateSuppressedNotificationsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateSuppressedNotificationsResponse, error) { rsp, err := c.UpdateSuppressedNotifications(ctx, clinicId, body, reqEditors...) if err != nil { @@ -10183,7 +13482,13 @@ func (c *ClientWithResponses) UpdateSuppressedNotificationsWithResponse(ctx cont return ParseUpdateSuppressedNotificationsResponse(rsp) } -// TideReportWithResponse request returning *TideReportResponse +// TideReportWithResponse View TIDE Report +// +// Retrieve a report of patients with tags by TIDE categories. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/clinics/{clinicId}/tide_report (the `TideReport` operationId). func (c *ClientWithResponses) TideReportWithResponse(ctx context.Context, clinicId ClinicId, params *TideReportParams, reqEditors ...RequestEditorFn) (*TideReportResponse, error) { rsp, err := c.TideReport(ctx, clinicId, params, reqEditors...) if err != nil { @@ -10192,7 +13497,13 @@ func (c *ClientWithResponses) TideReportWithResponse(ctx context.Context, clinic return ParseTideReportResponse(rsp) } -// UpdateTierWithBodyWithResponse request with arbitrary body returning *UpdateTierResponse +// UpdateTierWithBodyWithResponse Update Tier +// +// Internal endpoint to update clinic service tier. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/tier (the `UpdateTier` operationId). func (c *ClientWithResponses) UpdateTierWithBodyWithResponse(ctx context.Context, clinicId ClinicId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateTierResponse, error) { rsp, err := c.UpdateTierWithBody(ctx, clinicId, contentType, body, reqEditors...) if err != nil { @@ -10201,6 +13512,13 @@ func (c *ClientWithResponses) UpdateTierWithBodyWithResponse(ctx context.Context return ParseUpdateTierResponse(rsp) } +// UpdateTierWithResponse Update Tier +// +// Internal endpoint to update clinic service tier. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/clinics/{clinicId}/tier (the `UpdateTier` operationId). func (c *ClientWithResponses) UpdateTierWithResponse(ctx context.Context, clinicId ClinicId, body UpdateTierJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateTierResponse, error) { rsp, err := c.UpdateTier(ctx, clinicId, body, reqEditors...) if err != nil { @@ -10209,7 +13527,13 @@ func (c *ClientWithResponses) UpdateTierWithResponse(ctx context.Context, clinic return ParseUpdateTierResponse(rsp) } -// FindPatientsWithResponse request returning *FindPatientsResponse +// FindPatientsWithResponse Find Patients +// +// Returns all patients to which the clinician has access to. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/patients (the `FindPatients` operationId). func (c *ClientWithResponses) FindPatientsWithResponse(ctx context.Context, params *FindPatientsParams, reqEditors ...RequestEditorFn) (*FindPatientsResponse, error) { rsp, err := c.FindPatients(ctx, params, reqEditors...) if err != nil { @@ -10218,7 +13542,15 @@ func (c *ClientWithResponses) FindPatientsWithResponse(ctx context.Context, para return ParseFindPatientsResponse(rsp) } -// SyncEHRDataForPatientWithResponse request returning *SyncEHRDataForPatientResponse +// SyncEHRDataForPatientWithResponse Sync EHR Data for Patient +// +// An internal endpoint which will push the latest patient summary statistics and PDF reports to all EHR enabled clinics for which the patient has an active subscription. The actual data is pushed asynchronously. A successful response means that an asynchronous task has been scheduled. +// +// A 404 response will be returned if no active subscriptions can be found for given patient. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/patients/{patientId}/ehr/sync (the `SyncEHRDataForPatient` operationId). func (c *ClientWithResponses) SyncEHRDataForPatientWithResponse(ctx context.Context, patientId PatientId, reqEditors ...RequestEditorFn) (*SyncEHRDataForPatientResponse, error) { rsp, err := c.SyncEHRDataForPatient(ctx, patientId, reqEditors...) if err != nil { @@ -10227,7 +13559,13 @@ func (c *ClientWithResponses) SyncEHRDataForPatientWithResponse(ctx context.Cont return ParseSyncEHRDataForPatientResponse(rsp) } -// UpdatePatientSummaryWithBodyWithResponse request with arbitrary body returning *UpdatePatientSummaryResponse +// UpdatePatientSummaryWithBodyWithResponse UpdatePatientSummary +// +// Internal endpoint which updates the summary in all patient records which match the user id. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/patients/{patientId}/summary (the `UpdatePatientSummary` operationId). func (c *ClientWithResponses) UpdatePatientSummaryWithBodyWithResponse(ctx context.Context, patientId PatientId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientSummaryResponse, error) { rsp, err := c.UpdatePatientSummaryWithBody(ctx, patientId, contentType, body, reqEditors...) if err != nil { @@ -10236,6 +13574,13 @@ func (c *ClientWithResponses) UpdatePatientSummaryWithBodyWithResponse(ctx conte return ParseUpdatePatientSummaryResponse(rsp) } +// UpdatePatientSummaryWithResponse UpdatePatientSummary +// +// Internal endpoint which updates the summary in all patient records which match the user id. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/patients/{patientId}/summary (the `UpdatePatientSummary` operationId). func (c *ClientWithResponses) UpdatePatientSummaryWithResponse(ctx context.Context, patientId PatientId, body UpdatePatientSummaryJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientSummaryResponse, error) { rsp, err := c.UpdatePatientSummary(ctx, patientId, body, reqEditors...) if err != nil { @@ -10244,7 +13589,13 @@ func (c *ClientWithResponses) UpdatePatientSummaryWithResponse(ctx context.Conte return ParseUpdatePatientSummaryResponse(rsp) } -// ListClinicsForPatientWithResponse request returning *ListClinicsForPatientResponse +// ListClinicsForPatientWithResponse List Clinics for Patient +// +// Get all clinics a user is a patient of. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/patients/{userId}/clinics (the `ListClinicsForPatient` operationId). func (c *ClientWithResponses) ListClinicsForPatientWithResponse(ctx context.Context, userId UserId, params *ListClinicsForPatientParams, reqEditors ...RequestEditorFn) (*ListClinicsForPatientResponse, error) { rsp, err := c.ListClinicsForPatient(ctx, userId, params, reqEditors...) if err != nil { @@ -10253,7 +13604,13 @@ func (c *ClientWithResponses) ListClinicsForPatientWithResponse(ctx context.Cont return ParseListClinicsForPatientResponse(rsp) } -// UpdatePatientDataSourcesWithBodyWithResponse request with arbitrary body returning *UpdatePatientDataSourcesResponse +// UpdatePatientDataSourcesWithBodyWithResponse Create or update a data source for a patient +// +// Internal endpoint to upsert patient data source provider details in all their associated clinic records. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/patients/{userId}/data_sources (the `UpdatePatientDataSources` operationId). func (c *ClientWithResponses) UpdatePatientDataSourcesWithBodyWithResponse(ctx context.Context, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdatePatientDataSourcesResponse, error) { rsp, err := c.UpdatePatientDataSourcesWithBody(ctx, userId, contentType, body, reqEditors...) if err != nil { @@ -10262,6 +13619,13 @@ func (c *ClientWithResponses) UpdatePatientDataSourcesWithBodyWithResponse(ctx c return ParseUpdatePatientDataSourcesResponse(rsp) } +// UpdatePatientDataSourcesWithResponse Create or update a data source for a patient +// +// Internal endpoint to upsert patient data source provider details in all their associated clinic records. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/patients/{userId}/data_sources (the `UpdatePatientDataSources` operationId). func (c *ClientWithResponses) UpdatePatientDataSourcesWithResponse(ctx context.Context, userId UserId, body UpdatePatientDataSourcesJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdatePatientDataSourcesResponse, error) { rsp, err := c.UpdatePatientDataSources(ctx, userId, body, reqEditors...) if err != nil { @@ -10270,7 +13634,13 @@ func (c *ClientWithResponses) UpdatePatientDataSourcesWithResponse(ctx context.C return ParseUpdatePatientDataSourcesResponse(rsp) } -// ProcessEHRMessageWithBodyWithResponse request with arbitrary body returning *ProcessEHRMessageResponse +// ProcessEHRMessageWithBodyWithResponse Redox EHR Endpoint +// +// An endpoint for incoming Redox messages. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/redox (the `ProcessEHRMessage` operationId). func (c *ClientWithResponses) ProcessEHRMessageWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ProcessEHRMessageResponse, error) { rsp, err := c.ProcessEHRMessageWithBody(ctx, contentType, body, reqEditors...) if err != nil { @@ -10279,6 +13649,13 @@ func (c *ClientWithResponses) ProcessEHRMessageWithBodyWithResponse(ctx context. return ParseProcessEHRMessageResponse(rsp) } +// ProcessEHRMessageWithResponse Redox EHR Endpoint +// +// An endpoint for incoming Redox messages. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/redox (the `ProcessEHRMessage` operationId). func (c *ClientWithResponses) ProcessEHRMessageWithResponse(ctx context.Context, body ProcessEHRMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*ProcessEHRMessageResponse, error) { rsp, err := c.ProcessEHRMessage(ctx, body, reqEditors...) if err != nil { @@ -10287,7 +13664,19 @@ func (c *ClientWithResponses) ProcessEHRMessageWithResponse(ctx context.Context, return ParseProcessEHRMessageResponse(rsp) } -// MatchClinicAndPatientWithBodyWithResponse request with arbitrary body returning *MatchClinicAndPatientResponse +// MatchClinicAndPatientWithBodyWithResponse Match Clinic and Patient +// +// An internal endpoint used for matching an EHR request (e.g. NewOrder) to a clinic and patient. +// +// If a clinic with the provided details cannot be found, the service will return `404 Not Found` and will not attempt to match patients to the provided details. +// +// Due to lack of uniqueness constraints on the MRN and DOB fields it's possible that multiple patient records match the provided attributes. All results will be returned in the response and it's the calling services responsiblity to determine how to handle duplicates records. +// +// If a unique match is found, the patient matching criteria will be added to the patient record for future use (e.g. pushing data on a schedule). +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/redox/match (the `MatchClinicAndPatient` operationId). func (c *ClientWithResponses) MatchClinicAndPatientWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MatchClinicAndPatientResponse, error) { rsp, err := c.MatchClinicAndPatientWithBody(ctx, contentType, body, reqEditors...) if err != nil { @@ -10296,6 +13685,19 @@ func (c *ClientWithResponses) MatchClinicAndPatientWithBodyWithResponse(ctx cont return ParseMatchClinicAndPatientResponse(rsp) } +// MatchClinicAndPatientWithResponse Match Clinic and Patient +// +// An internal endpoint used for matching an EHR request (e.g. NewOrder) to a clinic and patient. +// +// If a clinic with the provided details cannot be found, the service will return `404 Not Found` and will not attempt to match patients to the provided details. +// +// Due to lack of uniqueness constraints on the MRN and DOB fields it's possible that multiple patient records match the provided attributes. All results will be returned in the response and it's the calling services responsiblity to determine how to handle duplicates records. +// +// If a unique match is found, the patient matching criteria will be added to the patient record for future use (e.g. pushing data on a schedule). +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/redox/match (the `MatchClinicAndPatient` operationId). func (c *ClientWithResponses) MatchClinicAndPatientWithResponse(ctx context.Context, body MatchClinicAndPatientJSONRequestBody, reqEditors ...RequestEditorFn) (*MatchClinicAndPatientResponse, error) { rsp, err := c.MatchClinicAndPatient(ctx, body, reqEditors...) if err != nil { @@ -10304,7 +13706,13 @@ func (c *ClientWithResponses) MatchClinicAndPatientWithResponse(ctx context.Cont return ParseMatchClinicAndPatientResponse(rsp) } -// VerifyEndpointWithResponse request returning *VerifyEndpointResponse +// VerifyEndpointWithResponse Redox Verify Endpoint +// +// Internal endpoint for Redox challenge verification. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/redox/verify (the `VerifyEndpoint` operationId). func (c *ClientWithResponses) VerifyEndpointWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*VerifyEndpointResponse, error) { rsp, err := c.VerifyEndpoint(ctx, reqEditors...) if err != nil { @@ -10313,7 +13721,13 @@ func (c *ClientWithResponses) VerifyEndpointWithResponse(ctx context.Context, re return ParseVerifyEndpointResponse(rsp) } -// DeletePatientSummaryWithResponse request returning *DeletePatientSummaryResponse +// DeletePatientSummaryWithResponse DeletePatientSummary +// +// Internal endpoint which deletes the summary in all patient records which match the summary id. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/summaries/{summaryId}/clinics (the `DeletePatientSummary` operationId). func (c *ClientWithResponses) DeletePatientSummaryWithResponse(ctx context.Context, summaryId SummaryId, reqEditors ...RequestEditorFn) (*DeletePatientSummaryResponse, error) { rsp, err := c.DeletePatientSummary(ctx, summaryId, reqEditors...) if err != nil { @@ -10322,7 +13736,23 @@ func (c *ClientWithResponses) DeletePatientSummaryWithResponse(ctx context.Conte return ParseDeletePatientSummaryResponse(rsp) } -// DeleteUserFromClinicsWithResponse request returning *DeleteUserFromClinicsResponse +// DeleteUserFromClinicsWithResponse Remove User from Clinics +// +// Internal endpoint that deletes any user associations with clinics they are a patient or a member of. +// +// If the user is a patient of a clinic the service will delete the patient-clinic relationship. +// +// If the user is one of many admins of any clinic the service will delete the clinician-clinic relationship. +// +// If the user is the only admin of a clinic the service will: +// - delete the clinician record, leaving the clinic without an admin +// - delete all non-custodial patient relationships of the clinic +// - keep all custodian relationships, allowing users to claim custodial accounts with an email +// - keep all custodian accounts and data even if they can’t be claimed when there isn’t an email address associated with the account +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /v1/users/{userId}/clinics (the `DeleteUserFromClinics` operationId). func (c *ClientWithResponses) DeleteUserFromClinicsWithResponse(ctx context.Context, userId UserId, reqEditors ...RequestEditorFn) (*DeleteUserFromClinicsResponse, error) { rsp, err := c.DeleteUserFromClinics(ctx, userId, reqEditors...) if err != nil { @@ -10331,7 +13761,13 @@ func (c *ClientWithResponses) DeleteUserFromClinicsWithResponse(ctx context.Cont return ParseDeleteUserFromClinicsResponse(rsp) } -// UpdateClinicUserDetailsWithBodyWithResponse request with arbitrary body returning *UpdateClinicUserDetailsResponse +// UpdateClinicUserDetailsWithBodyWithResponse Update User Details +// +// Internal endpoint to update clinician details. In the future it may be extended to support patient updates. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/users/{userId}/clinics (the `UpdateClinicUserDetails` operationId). func (c *ClientWithResponses) UpdateClinicUserDetailsWithBodyWithResponse(ctx context.Context, userId UserId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateClinicUserDetailsResponse, error) { rsp, err := c.UpdateClinicUserDetailsWithBody(ctx, userId, contentType, body, reqEditors...) if err != nil { @@ -10340,6 +13776,13 @@ func (c *ClientWithResponses) UpdateClinicUserDetailsWithBodyWithResponse(ctx co return ParseUpdateClinicUserDetailsResponse(rsp) } +// UpdateClinicUserDetailsWithResponse Update User Details +// +// Internal endpoint to update clinician details. In the future it may be extended to support patient updates. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/users/{userId}/clinics (the `UpdateClinicUserDetails` operationId). func (c *ClientWithResponses) UpdateClinicUserDetailsWithResponse(ctx context.Context, userId UserId, body UpdateClinicUserDetailsJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateClinicUserDetailsResponse, error) { rsp, err := c.UpdateClinicUserDetails(ctx, userId, body, reqEditors...) if err != nil { @@ -10348,7 +13791,13 @@ func (c *ClientWithResponses) UpdateClinicUserDetailsWithResponse(ctx context.Co return ParseUpdateClinicUserDetailsResponse(rsp) } -// XealthNotificationWithResponse request returning *XealthNotificationResponse +// XealthNotificationWithResponse Notification Webhook +// +// Notification webhook. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/xealth/notification (the `XealthNotification` operationId). func (c *ClientWithResponses) XealthNotificationWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*XealthNotificationResponse, error) { rsp, err := c.XealthNotification(ctx, reqEditors...) if err != nil { @@ -10357,7 +13806,15 @@ func (c *ClientWithResponses) XealthNotificationWithResponse(ctx context.Context return ParseXealthNotificationResponse(rsp) } -// XealthPreorderWithResponse request returning *XealthPreorderResponse +// XealthPreorderWithResponse Preorder Form Webhook +// +// The Preorder endpoint can be invoked when a care provider attempts to place an order manually within the EHR. Subscribers can use Preorder Forms to present additional information to, or receive input from, a care provider before an order can be placed. Preorder Forms can also be implemented so as to prevent an order from being placed entirely. +// +// https://sandbox.xealth.io/app/api-docs#tag/Digital-Care/paths/preorder/post +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /v1/xealth/preorder (the `XealthPreorder` operationId). func (c *ClientWithResponses) XealthPreorderWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*XealthPreorderResponse, error) { rsp, err := c.XealthPreorder(ctx, reqEditors...) if err != nil { @@ -10366,7 +13823,13 @@ func (c *ClientWithResponses) XealthPreorderWithResponse(ctx context.Context, re return ParseXealthPreorderResponse(rsp) } -// XealthGetProgramUrlWithResponse request returning *XealthGetProgramUrlResponse +// XealthGetProgramUrlWithResponse Get Program Url +// +// Get program URL. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/xealth/program (the `XealthGetProgramUrl` operationId). func (c *ClientWithResponses) XealthGetProgramUrlWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*XealthGetProgramUrlResponse, error) { rsp, err := c.XealthGetProgramUrl(ctx, reqEditors...) if err != nil { @@ -10375,7 +13838,13 @@ func (c *ClientWithResponses) XealthGetProgramUrlWithResponse(ctx context.Contex return ParseXealthGetProgramUrlResponse(rsp) } -// XealthGetProgramsWithResponse request returning *XealthGetProgramsResponse +// XealthGetProgramsWithResponse Get Programs +// +// Get programs. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /v1/xealth/programs (the `XealthGetPrograms` operationId). func (c *ClientWithResponses) XealthGetProgramsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*XealthGetProgramsResponse, error) { rsp, err := c.XealthGetPrograms(ctx, reqEditors...) if err != nil { @@ -10384,7 +13853,13 @@ func (c *ClientWithResponses) XealthGetProgramsWithResponse(ctx context.Context, return ParseXealthGetProgramsResponse(rsp) } -// ViewPDFReportWithResponse request returning *ViewPDFReportResponse +// ViewPDFReportWithResponse View PDF Report +// +// View PDF report for a given patient of a clinic. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /v1/xealth/report/web/viewer.html (the `ViewPDFReport` operationId). func (c *ClientWithResponses) ViewPDFReportWithResponse(ctx context.Context, params *ViewPDFReportParams, reqEditors ...RequestEditorFn) (*ViewPDFReportResponse, error) { rsp, err := c.ViewPDFReport(ctx, params, reqEditors...) if err != nil { @@ -10546,6 +14021,15 @@ func ParseCreateClinicResponse(rsp *http.Response) (*CreateClinicResponse, error } response.XML200 = &dest + case rsp.StatusCode == 400: + break // No content-type + + case rsp.StatusCode == 403: + break // No content-type + + case rsp.StatusCode == 500: + break // No content-type + } return response, nil @@ -10614,6 +14098,15 @@ func ParseGetClinicResponse(rsp *http.Response) (*GetClinicResponse, error) { } response.JSON200 = &dest + case rsp.StatusCode == 400: + break // No content-type + + case rsp.StatusCode == 403: + break // No content-type + + case rsp.StatusCode == 500: + break // No content-type + } return response, nil @@ -10640,6 +14133,15 @@ func ParseUpdateClinicResponse(rsp *http.Response) (*UpdateClinicResponse, error } response.JSON200 = &dest + case rsp.StatusCode == 400: + break // No content-type + + case rsp.StatusCode == 403: + break // No content-type + + case rsp.StatusCode == 500: + break // No content-type + } return response, nil @@ -11158,6 +14660,9 @@ func ParseConvertPatientTagToSiteResponse(rsp *http.Response) (*ConvertPatientTa } response.JSON200 = &dest + case rsp.StatusCode == 409: + break // No content-type + } return response, nil @@ -11420,6 +14925,12 @@ func ParseDeletePatientReviewsResponse(rsp *http.Response) (*DeletePatientReview } response.JSON200 = &dest + case rsp.StatusCode == 404: + break // No content-type + + case rsp.StatusCode == 409: + break // No content-type + } return response, nil @@ -11914,6 +15425,9 @@ func ParseMatchClinicAndPatientResponse(rsp *http.Response) (*MatchClinicAndPati } response.JSON200 = &dest + case rsp.StatusCode == 404: + break // No content-type + } return response, nil diff --git a/client/go.mod b/client/go.mod index 08e34ac86..45864fe0e 100644 --- a/client/go.mod +++ b/client/go.mod @@ -3,13 +3,13 @@ module github.com/tidepool-org/clinic/client go 1.24.1 require ( - github.com/oapi-codegen/runtime v1.1.1 + github.com/oapi-codegen/runtime v1.6.0 go.uber.org/mock v0.5.0 ) require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/google/uuid v1.5.0 // indirect + github.com/google/uuid v1.6.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/tools v0.24.1 // indirect diff --git a/client/go.sum b/client/go.sum index 910f02770..fb7e03e9c 100644 --- a/client/go.sum +++ b/client/go.sum @@ -5,18 +5,20 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= -github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= -github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/oapi-codegen/nullable v1.1.0 h1:eAh8JVc5430VtYVnq00Hrbpag9PFRGWLjxR1/3KntMs= +github.com/oapi-codegen/nullable v1.1.0/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY= +github.com/oapi-codegen/runtime v1.6.0 h1:7Xx+GlueD6nRuyKoCPzL434Jfi3BetbiJOrzCHp/VPU= +github.com/oapi-codegen/runtime v1.6.0/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= diff --git a/client/types.go b/client/types.go index 7c114f770..1a810677c 100644 --- a/client/types.go +++ b/client/types.go @@ -1,6 +1,6 @@ // Package client provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.8.0 DO NOT EDIT. package client import ( @@ -9,10 +9,6 @@ import ( openapi_types "github.com/oapi-codegen/runtime/types" ) -const ( - SessionTokenScopes = "sessionToken.Scopes" -) - // Defines values for ClinicV1ClinicSize. const ( N0249 ClinicV1ClinicSize = "0-249" @@ -21,6 +17,22 @@ const ( N500999 ClinicV1ClinicSize = "500-999" ) +// Valid indicates whether the value is a known member of the ClinicV1ClinicSize enum. +func (e ClinicV1ClinicSize) Valid() bool { + switch e { + case N0249: + return true + case N1000: + return true + case N250499: + return true + case N500999: + return true + default: + return false + } +} + // Defines values for ClinicV1ClinicType. const ( ClinicV1ClinicTypeHealthcareSystem ClinicV1ClinicType = "healthcare_system" @@ -30,12 +42,42 @@ const ( ClinicV1ClinicTypeVeterinaryClinic ClinicV1ClinicType = "veterinary_clinic" ) +// Valid indicates whether the value is a known member of the ClinicV1ClinicType enum. +func (e ClinicV1ClinicType) Valid() bool { + switch e { + case ClinicV1ClinicTypeHealthcareSystem: + return true + case ClinicV1ClinicTypeOther: + return true + case ClinicV1ClinicTypeProviderPractice: + return true + case ClinicV1ClinicTypeResearcher: + return true + case ClinicV1ClinicTypeVeterinaryClinic: + return true + default: + return false + } +} + // Defines values for ClinicV1PreferredBgUnits. const ( ClinicV1PreferredBgUnitsMgdL ClinicV1PreferredBgUnits = "mg/dL" ClinicV1PreferredBgUnitsMmolL ClinicV1PreferredBgUnits = "mmol/L" ) +// Valid indicates whether the value is a known member of the ClinicV1PreferredBgUnits enum. +func (e ClinicV1PreferredBgUnits) Valid() bool { + switch e { + case ClinicV1PreferredBgUnitsMgdL: + return true + case ClinicV1PreferredBgUnitsMmolL: + return true + default: + return false + } +} + // Defines values for ClinicTimezoneV1. const ( AfricaAbidjan ClinicTimezoneV1 = "Africa/Abidjan" @@ -635,6 +677,1204 @@ const ( Zulu ClinicTimezoneV1 = "Zulu" ) +// Valid indicates whether the value is a known member of the ClinicTimezoneV1 enum. +func (e ClinicTimezoneV1) Valid() bool { + switch e { + case AfricaAbidjan: + return true + case AfricaAccra: + return true + case AfricaAddisAbaba: + return true + case AfricaAlgiers: + return true + case AfricaAsmara: + return true + case AfricaAsmera: + return true + case AfricaBamako: + return true + case AfricaBangui: + return true + case AfricaBanjul: + return true + case AfricaBissau: + return true + case AfricaBlantyre: + return true + case AfricaBrazzaville: + return true + case AfricaBujumbura: + return true + case AfricaCairo: + return true + case AfricaCasablanca: + return true + case AfricaCeuta: + return true + case AfricaConakry: + return true + case AfricaDakar: + return true + case AfricaDarEsSalaam: + return true + case AfricaDjibouti: + return true + case AfricaDouala: + return true + case AfricaElAaiun: + return true + case AfricaFreetown: + return true + case AfricaGaborone: + return true + case AfricaHarare: + return true + case AfricaJohannesburg: + return true + case AfricaJuba: + return true + case AfricaKampala: + return true + case AfricaKhartoum: + return true + case AfricaKigali: + return true + case AfricaKinshasa: + return true + case AfricaLagos: + return true + case AfricaLibreville: + return true + case AfricaLome: + return true + case AfricaLuanda: + return true + case AfricaLubumbashi: + return true + case AfricaLusaka: + return true + case AfricaMalabo: + return true + case AfricaMaputo: + return true + case AfricaMaseru: + return true + case AfricaMbabane: + return true + case AfricaMogadishu: + return true + case AfricaMonrovia: + return true + case AfricaNairobi: + return true + case AfricaNdjamena: + return true + case AfricaNiamey: + return true + case AfricaNouakchott: + return true + case AfricaOuagadougou: + return true + case AfricaPortoNovo: + return true + case AfricaSaoTome: + return true + case AfricaTimbuktu: + return true + case AfricaTripoli: + return true + case AfricaTunis: + return true + case AfricaWindhoek: + return true + case AmericaAdak: + return true + case AmericaAnchorage: + return true + case AmericaAnguilla: + return true + case AmericaAntigua: + return true + case AmericaAraguaina: + return true + case AmericaArgentinaBuenosAires: + return true + case AmericaArgentinaCatamarca: + return true + case AmericaArgentinaComodRivadavia: + return true + case AmericaArgentinaCordoba: + return true + case AmericaArgentinaJujuy: + return true + case AmericaArgentinaLaRioja: + return true + case AmericaArgentinaMendoza: + return true + case AmericaArgentinaRioGallegos: + return true + case AmericaArgentinaSalta: + return true + case AmericaArgentinaSanJuan: + return true + case AmericaArgentinaSanLuis: + return true + case AmericaArgentinaTucuman: + return true + case AmericaArgentinaUshuaia: + return true + case AmericaAruba: + return true + case AmericaAsuncion: + return true + case AmericaAtikokan: + return true + case AmericaAtka: + return true + case AmericaBahia: + return true + case AmericaBahiaBanderas: + return true + case AmericaBarbados: + return true + case AmericaBelem: + return true + case AmericaBelize: + return true + case AmericaBlancSablon: + return true + case AmericaBoaVista: + return true + case AmericaBogota: + return true + case AmericaBoise: + return true + case AmericaBuenosAires: + return true + case AmericaCambridgeBay: + return true + case AmericaCampoGrande: + return true + case AmericaCancun: + return true + case AmericaCaracas: + return true + case AmericaCatamarca: + return true + case AmericaCayenne: + return true + case AmericaCayman: + return true + case AmericaChicago: + return true + case AmericaChihuahua: + return true + case AmericaCiudadJuarez: + return true + case AmericaCoralHarbour: + return true + case AmericaCordoba: + return true + case AmericaCostaRica: + return true + case AmericaCreston: + return true + case AmericaCuiaba: + return true + case AmericaCuracao: + return true + case AmericaDanmarkshavn: + return true + case AmericaDawson: + return true + case AmericaDawsonCreek: + return true + case AmericaDenver: + return true + case AmericaDetroit: + return true + case AmericaDominica: + return true + case AmericaEdmonton: + return true + case AmericaEirunepe: + return true + case AmericaElSalvador: + return true + case AmericaEnsenada: + return true + case AmericaFortNelson: + return true + case AmericaFortWayne: + return true + case AmericaFortaleza: + return true + case AmericaGlaceBay: + return true + case AmericaGodthab: + return true + case AmericaGooseBay: + return true + case AmericaGrandTurk: + return true + case AmericaGrenada: + return true + case AmericaGuadeloupe: + return true + case AmericaGuatemala: + return true + case AmericaGuayaquil: + return true + case AmericaGuyana: + return true + case AmericaHalifax: + return true + case AmericaHavana: + return true + case AmericaHermosillo: + return true + case AmericaIndianaIndianapolis: + return true + case AmericaIndianaKnox: + return true + case AmericaIndianaMarengo: + return true + case AmericaIndianaPetersburg: + return true + case AmericaIndianaTellCity: + return true + case AmericaIndianaVevay: + return true + case AmericaIndianaVincennes: + return true + case AmericaIndianaWinamac: + return true + case AmericaIndianapolis: + return true + case AmericaInuvik: + return true + case AmericaIqaluit: + return true + case AmericaJamaica: + return true + case AmericaJujuy: + return true + case AmericaJuneau: + return true + case AmericaKentuckyLouisville: + return true + case AmericaKentuckyMonticello: + return true + case AmericaKnoxIN: + return true + case AmericaKralendijk: + return true + case AmericaLaPaz: + return true + case AmericaLima: + return true + case AmericaLosAngeles: + return true + case AmericaLouisville: + return true + case AmericaLowerPrinces: + return true + case AmericaMaceio: + return true + case AmericaManagua: + return true + case AmericaManaus: + return true + case AmericaMarigot: + return true + case AmericaMartinique: + return true + case AmericaMatamoros: + return true + case AmericaMazatlan: + return true + case AmericaMendoza: + return true + case AmericaMenominee: + return true + case AmericaMerida: + return true + case AmericaMetlakatla: + return true + case AmericaMexicoCity: + return true + case AmericaMiquelon: + return true + case AmericaMoncton: + return true + case AmericaMonterrey: + return true + case AmericaMontevideo: + return true + case AmericaMontreal: + return true + case AmericaMontserrat: + return true + case AmericaNassau: + return true + case AmericaNewYork: + return true + case AmericaNipigon: + return true + case AmericaNome: + return true + case AmericaNoronha: + return true + case AmericaNorthDakotaBeulah: + return true + case AmericaNorthDakotaCenter: + return true + case AmericaNorthDakotaNewSalem: + return true + case AmericaNuuk: + return true + case AmericaOjinaga: + return true + case AmericaPanama: + return true + case AmericaPangnirtung: + return true + case AmericaParamaribo: + return true + case AmericaPhoenix: + return true + case AmericaPortAuPrince: + return true + case AmericaPortOfSpain: + return true + case AmericaPortoAcre: + return true + case AmericaPortoVelho: + return true + case AmericaPuertoRico: + return true + case AmericaPuntaArenas: + return true + case AmericaRainyRiver: + return true + case AmericaRankinInlet: + return true + case AmericaRecife: + return true + case AmericaRegina: + return true + case AmericaResolute: + return true + case AmericaRioBranco: + return true + case AmericaRosario: + return true + case AmericaSantaIsabel: + return true + case AmericaSantarem: + return true + case AmericaSantiago: + return true + case AmericaSantoDomingo: + return true + case AmericaSaoPaulo: + return true + case AmericaScoresbysund: + return true + case AmericaShiprock: + return true + case AmericaSitka: + return true + case AmericaStBarthelemy: + return true + case AmericaStJohns: + return true + case AmericaStKitts: + return true + case AmericaStLucia: + return true + case AmericaStThomas: + return true + case AmericaStVincent: + return true + case AmericaSwiftCurrent: + return true + case AmericaTegucigalpa: + return true + case AmericaThule: + return true + case AmericaThunderBay: + return true + case AmericaTijuana: + return true + case AmericaToronto: + return true + case AmericaTortola: + return true + case AmericaVancouver: + return true + case AmericaVirgin: + return true + case AmericaWhitehorse: + return true + case AmericaWinnipeg: + return true + case AmericaYakutat: + return true + case AmericaYellowknife: + return true + case AntarcticaCasey: + return true + case AntarcticaDavis: + return true + case AntarcticaDumontDUrville: + return true + case AntarcticaMacquarie: + return true + case AntarcticaMawson: + return true + case AntarcticaMcMurdo: + return true + case AntarcticaPalmer: + return true + case AntarcticaRothera: + return true + case AntarcticaSouthPole: + return true + case AntarcticaSyowa: + return true + case AntarcticaTroll: + return true + case AntarcticaVostok: + return true + case ArcticLongyearbyen: + return true + case AsiaAden: + return true + case AsiaAlmaty: + return true + case AsiaAmman: + return true + case AsiaAnadyr: + return true + case AsiaAqtau: + return true + case AsiaAqtobe: + return true + case AsiaAshgabat: + return true + case AsiaAshkhabad: + return true + case AsiaAtyrau: + return true + case AsiaBaghdad: + return true + case AsiaBahrain: + return true + case AsiaBaku: + return true + case AsiaBangkok: + return true + case AsiaBarnaul: + return true + case AsiaBeirut: + return true + case AsiaBishkek: + return true + case AsiaBrunei: + return true + case AsiaCalcutta: + return true + case AsiaChita: + return true + case AsiaChoibalsan: + return true + case AsiaChongqing: + return true + case AsiaChungking: + return true + case AsiaColombo: + return true + case AsiaDacca: + return true + case AsiaDamascus: + return true + case AsiaDhaka: + return true + case AsiaDili: + return true + case AsiaDubai: + return true + case AsiaDushanbe: + return true + case AsiaFamagusta: + return true + case AsiaGaza: + return true + case AsiaHarbin: + return true + case AsiaHebron: + return true + case AsiaHoChiMinh: + return true + case AsiaHongKong: + return true + case AsiaHovd: + return true + case AsiaIrkutsk: + return true + case AsiaIstanbul: + return true + case AsiaJakarta: + return true + case AsiaJayapura: + return true + case AsiaJerusalem: + return true + case AsiaKabul: + return true + case AsiaKamchatka: + return true + case AsiaKarachi: + return true + case AsiaKashgar: + return true + case AsiaKathmandu: + return true + case AsiaKatmandu: + return true + case AsiaKhandyga: + return true + case AsiaKolkata: + return true + case AsiaKrasnoyarsk: + return true + case AsiaKualaLumpur: + return true + case AsiaKuching: + return true + case AsiaKuwait: + return true + case AsiaMacao: + return true + case AsiaMacau: + return true + case AsiaMagadan: + return true + case AsiaMakassar: + return true + case AsiaManila: + return true + case AsiaMuscat: + return true + case AsiaNicosia: + return true + case AsiaNovokuznetsk: + return true + case AsiaNovosibirsk: + return true + case AsiaOmsk: + return true + case AsiaOral: + return true + case AsiaPhnomPenh: + return true + case AsiaPontianak: + return true + case AsiaPyongyang: + return true + case AsiaQatar: + return true + case AsiaQostanay: + return true + case AsiaQyzylorda: + return true + case AsiaRangoon: + return true + case AsiaRiyadh: + return true + case AsiaSaigon: + return true + case AsiaSakhalin: + return true + case AsiaSamarkand: + return true + case AsiaSeoul: + return true + case AsiaShanghai: + return true + case AsiaSingapore: + return true + case AsiaSrednekolymsk: + return true + case AsiaTaipei: + return true + case AsiaTashkent: + return true + case AsiaTbilisi: + return true + case AsiaTehran: + return true + case AsiaTelAviv: + return true + case AsiaThimbu: + return true + case AsiaThimphu: + return true + case AsiaTokyo: + return true + case AsiaTomsk: + return true + case AsiaUjungPandang: + return true + case AsiaUlaanbaatar: + return true + case AsiaUlanBator: + return true + case AsiaUrumqi: + return true + case AsiaUstNera: + return true + case AsiaVientiane: + return true + case AsiaVladivostok: + return true + case AsiaYakutsk: + return true + case AsiaYangon: + return true + case AsiaYekaterinburg: + return true + case AsiaYerevan: + return true + case AtlanticAzores: + return true + case AtlanticBermuda: + return true + case AtlanticCanary: + return true + case AtlanticCapeVerde: + return true + case AtlanticFaeroe: + return true + case AtlanticFaroe: + return true + case AtlanticJanMayen: + return true + case AtlanticMadeira: + return true + case AtlanticReykjavik: + return true + case AtlanticSouthGeorgia: + return true + case AtlanticStHelena: + return true + case AtlanticStanley: + return true + case AustraliaACT: + return true + case AustraliaAdelaide: + return true + case AustraliaBrisbane: + return true + case AustraliaBrokenHill: + return true + case AustraliaCanberra: + return true + case AustraliaCurrie: + return true + case AustraliaDarwin: + return true + case AustraliaEucla: + return true + case AustraliaHobart: + return true + case AustraliaLHI: + return true + case AustraliaLindeman: + return true + case AustraliaLordHowe: + return true + case AustraliaMelbourne: + return true + case AustraliaNSW: + return true + case AustraliaNorth: + return true + case AustraliaPerth: + return true + case AustraliaQueensland: + return true + case AustraliaSouth: + return true + case AustraliaSydney: + return true + case AustraliaTasmania: + return true + case AustraliaVictoria: + return true + case AustraliaWest: + return true + case AustraliaYancowinna: + return true + case BrazilAcre: + return true + case BrazilDeNoronha: + return true + case BrazilEast: + return true + case BrazilWest: + return true + case CET: + return true + case CST6CDT: + return true + case CanadaAtlantic: + return true + case CanadaCentral: + return true + case CanadaEastern: + return true + case CanadaMountain: + return true + case CanadaNewfoundland: + return true + case CanadaPacific: + return true + case CanadaSaskatchewan: + return true + case CanadaYukon: + return true + case ChileContinental: + return true + case ChileEasterIsland: + return true + case Cuba: + return true + case EET: + return true + case EST: + return true + case EST5EDT: + return true + case Egypt: + return true + case Eire: + return true + case EtcGMT: + return true + case EtcGMT0: + return true + case EtcGMT01: + return true + case EtcGMT02: + return true + case EtcGMT1: + return true + case EtcGMT10: + return true + case EtcGMT101: + return true + case EtcGMT11: + return true + case EtcGMT111: + return true + case EtcGMT12: + return true + case EtcGMT121: + return true + case EtcGMT13: + return true + case EtcGMT14: + return true + case EtcGMT2: + return true + case EtcGMT21: + return true + case EtcGMT3: + return true + case EtcGMT31: + return true + case EtcGMT4: + return true + case EtcGMT41: + return true + case EtcGMT5: + return true + case EtcGMT51: + return true + case EtcGMT6: + return true + case EtcGMT61: + return true + case EtcGMT7: + return true + case EtcGMT71: + return true + case EtcGMT8: + return true + case EtcGMT81: + return true + case EtcGMT9: + return true + case EtcGMT91: + return true + case EtcGreenwich: + return true + case EtcUCT: + return true + case EtcUTC: + return true + case EtcUniversal: + return true + case EtcZulu: + return true + case EuropeAmsterdam: + return true + case EuropeAndorra: + return true + case EuropeAstrakhan: + return true + case EuropeAthens: + return true + case EuropeBelfast: + return true + case EuropeBelgrade: + return true + case EuropeBerlin: + return true + case EuropeBratislava: + return true + case EuropeBrussels: + return true + case EuropeBucharest: + return true + case EuropeBudapest: + return true + case EuropeBusingen: + return true + case EuropeChisinau: + return true + case EuropeCopenhagen: + return true + case EuropeDublin: + return true + case EuropeGibraltar: + return true + case EuropeGuernsey: + return true + case EuropeHelsinki: + return true + case EuropeIsleOfMan: + return true + case EuropeIstanbul: + return true + case EuropeJersey: + return true + case EuropeKaliningrad: + return true + case EuropeKiev: + return true + case EuropeKirov: + return true + case EuropeKyiv: + return true + case EuropeLisbon: + return true + case EuropeLjubljana: + return true + case EuropeLondon: + return true + case EuropeLuxembourg: + return true + case EuropeMadrid: + return true + case EuropeMalta: + return true + case EuropeMariehamn: + return true + case EuropeMinsk: + return true + case EuropeMonaco: + return true + case EuropeMoscow: + return true + case EuropeNicosia: + return true + case EuropeOslo: + return true + case EuropeParis: + return true + case EuropePodgorica: + return true + case EuropePrague: + return true + case EuropeRiga: + return true + case EuropeRome: + return true + case EuropeSamara: + return true + case EuropeSanMarino: + return true + case EuropeSarajevo: + return true + case EuropeSaratov: + return true + case EuropeSimferopol: + return true + case EuropeSkopje: + return true + case EuropeSofia: + return true + case EuropeStockholm: + return true + case EuropeTallinn: + return true + case EuropeTirane: + return true + case EuropeTiraspol: + return true + case EuropeUlyanovsk: + return true + case EuropeUzhgorod: + return true + case EuropeVaduz: + return true + case EuropeVatican: + return true + case EuropeVienna: + return true + case EuropeVilnius: + return true + case EuropeVolgograd: + return true + case EuropeWarsaw: + return true + case EuropeZagreb: + return true + case EuropeZaporozhye: + return true + case EuropeZurich: + return true + case GB: + return true + case GBEire: + return true + case GMT: + return true + case GMT0: + return true + case GMT01: + return true + case GMT02: + return true + case Greenwich: + return true + case HST: + return true + case Hongkong: + return true + case Iceland: + return true + case IndianAntananarivo: + return true + case IndianChagos: + return true + case IndianChristmas: + return true + case IndianCocos: + return true + case IndianComoro: + return true + case IndianKerguelen: + return true + case IndianMahe: + return true + case IndianMaldives: + return true + case IndianMauritius: + return true + case IndianMayotte: + return true + case IndianReunion: + return true + case Iran: + return true + case Israel: + return true + case Jamaica: + return true + case Japan: + return true + case Kwajalein: + return true + case Libya: + return true + case MET: + return true + case MST: + return true + case MST7MDT: + return true + case MexicoBajaNorte: + return true + case MexicoBajaSur: + return true + case MexicoGeneral: + return true + case NZ: + return true + case NZCHAT: + return true + case Navajo: + return true + case PRC: + return true + case PST8PDT: + return true + case PacificApia: + return true + case PacificAuckland: + return true + case PacificBougainville: + return true + case PacificChatham: + return true + case PacificChuuk: + return true + case PacificEaster: + return true + case PacificEfate: + return true + case PacificEnderbury: + return true + case PacificFakaofo: + return true + case PacificFiji: + return true + case PacificFunafuti: + return true + case PacificGalapagos: + return true + case PacificGambier: + return true + case PacificGuadalcanal: + return true + case PacificGuam: + return true + case PacificHonolulu: + return true + case PacificJohnston: + return true + case PacificKanton: + return true + case PacificKiritimati: + return true + case PacificKosrae: + return true + case PacificKwajalein: + return true + case PacificMajuro: + return true + case PacificMarquesas: + return true + case PacificMidway: + return true + case PacificNauru: + return true + case PacificNiue: + return true + case PacificNorfolk: + return true + case PacificNoumea: + return true + case PacificPagoPago: + return true + case PacificPalau: + return true + case PacificPitcairn: + return true + case PacificPohnpei: + return true + case PacificPonape: + return true + case PacificPortMoresby: + return true + case PacificRarotonga: + return true + case PacificSaipan: + return true + case PacificSamoa: + return true + case PacificTahiti: + return true + case PacificTarawa: + return true + case PacificTongatapu: + return true + case PacificTruk: + return true + case PacificWake: + return true + case PacificWallis: + return true + case PacificYap: + return true + case Poland: + return true + case Portugal: + return true + case ROC: + return true + case ROK: + return true + case Singapore: + return true + case Turkey: + return true + case UCT: + return true + case USAlaska: + return true + case USAleutian: + return true + case USArizona: + return true + case USCentral: + return true + case USEastIndiana: + return true + case USEastern: + return true + case USHawaii: + return true + case USIndianaStarke: + return true + case USMichigan: + return true + case USMountain: + return true + case USPacific: + return true + case USSamoa: + return true + case UTC: + return true + case Universal: + return true + case WET: + return true + case WSU: + return true + case Zulu: + return true + default: + return false + } +} + // Defines values for DataSourceV1State. const ( Connected DataSourceV1State = "connected" @@ -644,6 +1884,24 @@ const ( PendingReconnect DataSourceV1State = "pendingReconnect" ) +// Valid indicates whether the value is a known member of the DataSourceV1State enum. +func (e DataSourceV1State) Valid() bool { + switch e { + case Connected: + return true + case Disconnected: + return true + case Error: + return true + case Pending: + return true + case PendingReconnect: + return true + default: + return false + } +} + // Defines values for DiagnosisTypeV1. const ( DiagnosisTypeV1Empty DiagnosisTypeV1 = "" @@ -658,16 +1916,64 @@ const ( DiagnosisTypeV1Type3c DiagnosisTypeV1 = "type3c" ) +// Valid indicates whether the value is a known member of the DiagnosisTypeV1 enum. +func (e DiagnosisTypeV1) Valid() bool { + switch e { + case DiagnosisTypeV1Empty: + return true + case DiagnosisTypeV1Gestational: + return true + case DiagnosisTypeV1Lada: + return true + case DiagnosisTypeV1Mody: + return true + case DiagnosisTypeV1NotApplicable: + return true + case DiagnosisTypeV1Other: + return true + case DiagnosisTypeV1Prediabetes: + return true + case DiagnosisTypeV1Type1: + return true + case DiagnosisTypeV1Type2: + return true + case DiagnosisTypeV1Type3c: + return true + default: + return false + } +} + // Defines values for EhrMatchMessageRefV1DataModel. const ( Order EhrMatchMessageRefV1DataModel = "Order" ) +// Valid indicates whether the value is a known member of the EhrMatchMessageRefV1DataModel enum. +func (e EhrMatchMessageRefV1DataModel) Valid() bool { + switch e { + case Order: + return true + default: + return false + } +} + // Defines values for EhrMatchMessageRefV1EventType. const ( EhrMatchMessageRefV1EventTypeNew EhrMatchMessageRefV1EventType = "New" ) +// Valid indicates whether the value is a known member of the EhrMatchMessageRefV1EventType enum. +func (e EhrMatchMessageRefV1EventType) Valid() bool { + switch e { + case EhrMatchMessageRefV1EventTypeNew: + return true + default: + return false + } +} + // Defines values for EhrMatchRequestPatientsOptionsV1Criteria. const ( DOBFULLNAME EhrMatchRequestPatientsOptionsV1Criteria = "DOB_FULLNAME" @@ -675,24 +1981,74 @@ const ( MRNDOB EhrMatchRequestPatientsOptionsV1Criteria = "MRN_DOB" ) +// Valid indicates whether the value is a known member of the EhrMatchRequestPatientsOptionsV1Criteria enum. +func (e EhrMatchRequestPatientsOptionsV1Criteria) Valid() bool { + switch e { + case DOBFULLNAME: + return true + case MRN: + return true + case MRNDOB: + return true + default: + return false + } +} + // Defines values for EhrMatchRequestPatientsOptionsV1OnUniqueMatch. const ( DISABLEREPORTS EhrMatchRequestPatientsOptionsV1OnUniqueMatch = "DISABLE_REPORTS" ENABLEREPORTS EhrMatchRequestPatientsOptionsV1OnUniqueMatch = "ENABLE_REPORTS" ) +// Valid indicates whether the value is a known member of the EhrMatchRequestPatientsOptionsV1OnUniqueMatch enum. +func (e EhrMatchRequestPatientsOptionsV1OnUniqueMatch) Valid() bool { + switch e { + case DISABLEREPORTS: + return true + case ENABLEREPORTS: + return true + default: + return false + } +} + // Defines values for EhrSettingsV1Provider. const ( Redox EhrSettingsV1Provider = "redox" Xealth EhrSettingsV1Provider = "xealth" ) +// Valid indicates whether the value is a known member of the EhrSettingsV1Provider enum. +func (e EhrSettingsV1Provider) Valid() bool { + switch e { + case Redox: + return true + case Xealth: + return true + default: + return false + } +} + // Defines values for GlycemicRangesV1Type. const ( Custom GlycemicRangesV1Type = "custom" Preset GlycemicRangesV1Type = "preset" ) +// Valid indicates whether the value is a known member of the GlycemicRangesV1Type enum. +func (e GlycemicRangesV1Type) Valid() bool { + switch e { + case Custom: + return true + case Preset: + return true + default: + return false + } +} + // Defines values for GlycemicRangesPresetV1. const ( ADAHighRisk GlycemicRangesPresetV1 = "adaHighRisk" @@ -701,12 +2057,40 @@ const ( ADAStandard GlycemicRangesPresetV1 = "adaStandard" ) +// Valid indicates whether the value is a known member of the GlycemicRangesPresetV1 enum. +func (e GlycemicRangesPresetV1) Valid() bool { + switch e { + case ADAHighRisk: + return true + case ADAPregnancyType1: + return true + case ADAPregnancyType2: + return true + case ADAStandard: + return true + default: + return false + } +} + // Defines values for GlycemicRangesThresholdUpperBoundV1Units. const ( GlycemicRangesThresholdUpperBoundV1UnitsMgdL GlycemicRangesThresholdUpperBoundV1Units = "mg/dL" GlycemicRangesThresholdUpperBoundV1UnitsMmolL GlycemicRangesThresholdUpperBoundV1Units = "mmol/L" ) +// Valid indicates whether the value is a known member of the GlycemicRangesThresholdUpperBoundV1Units enum. +func (e GlycemicRangesThresholdUpperBoundV1Units) Valid() bool { + switch e { + case GlycemicRangesThresholdUpperBoundV1UnitsMgdL: + return true + case GlycemicRangesThresholdUpperBoundV1UnitsMmolL: + return true + default: + return false + } +} + // Defines values for MigrationStatusV1. const ( COMPLETED MigrationStatusV1 = "COMPLETED" @@ -714,6 +2098,20 @@ const ( RUNNING MigrationStatusV1 = "RUNNING" ) +// Valid indicates whether the value is a known member of the MigrationStatusV1 enum. +func (e MigrationStatusV1) Valid() bool { + switch e { + case COMPLETED: + return true + case PENDING: + return true + case RUNNING: + return true + default: + return false + } +} + // Defines values for ProviderIdV1. const ( Abbott ProviderIdV1 = "abbott" @@ -721,6 +2119,20 @@ const ( Twiist ProviderIdV1 = "twiist" ) +// Valid indicates whether the value is a known member of the ProviderIdV1 enum. +func (e ProviderIdV1) Valid() bool { + switch e { + case Abbott: + return true + case Dexcom: + return true + case Twiist: + return true + default: + return false + } +} + // Defines values for ScheduledReportsV1Cadence. const ( DISABLED ScheduledReportsV1Cadence = "DISABLED" @@ -730,12 +2142,42 @@ const ( N7d ScheduledReportsV1Cadence = "7d" ) +// Valid indicates whether the value is a known member of the ScheduledReportsV1Cadence enum. +func (e ScheduledReportsV1Cadence) Valid() bool { + switch e { + case DISABLED: + return true + case N14d: + return true + case N1d: + return true + case N30d: + return true + case N7d: + return true + default: + return false + } +} + // Defines values for ScheduledReportsV1OnUploadNoteEventType. const ( ScheduledReportsV1OnUploadNoteEventTypeNew ScheduledReportsV1OnUploadNoteEventType = "New" ScheduledReportsV1OnUploadNoteEventTypeReplace ScheduledReportsV1OnUploadNoteEventType = "Replace" ) +// Valid indicates whether the value is a known member of the ScheduledReportsV1OnUploadNoteEventType enum. +func (e ScheduledReportsV1OnUploadNoteEventType) Valid() bool { + switch e { + case ScheduledReportsV1OnUploadNoteEventTypeNew: + return true + case ScheduledReportsV1OnUploadNoteEventTypeReplace: + return true + default: + return false + } +} + // Defines values for TierV1. const ( Tier0100 TierV1 = "tier0100" @@ -744,6 +2186,22 @@ const ( Tier0400 TierV1 = "tier0400" ) +// Valid indicates whether the value is a known member of the TierV1 enum. +func (e TierV1) Valid() bool { + switch e { + case Tier0100: + return true + case Tier0200: + return true + case Tier0300: + return true + case Tier0400: + return true + default: + return false + } +} + // Defines values for TideReportParamsCategories. const ( DropInTimeInTargetPercent TideReportParamsCategories = "dropInTimeInTargetPercent" @@ -757,16 +2215,54 @@ const ( TimeInVeryLowPercent TideReportParamsCategories = "timeInVeryLowPercent" ) +// Valid indicates whether the value is a known member of the TideReportParamsCategories enum. +func (e TideReportParamsCategories) Valid() bool { + switch e { + case DropInTimeInTargetPercent: + return true + case MeetingTargets: + return true + case TimeCGMUsePercent: + return true + case TimeInAnyHighPercent: + return true + case TimeInAnyLowPercent: + return true + case TimeInExtremeHighPercent: + return true + case TimeInTargetPercent: + return true + case TimeInVeryHighPercent: + return true + case TimeInVeryLowPercent: + return true + default: + return false + } +} + // Defines values for FindPatientsParamsWorkspaceIdType. const ( FindPatientsParamsWorkspaceIdTypeClinicId FindPatientsParamsWorkspaceIdType = "clinicId" FindPatientsParamsWorkspaceIdTypeEhrSourceId FindPatientsParamsWorkspaceIdType = "ehrSourceId" ) -// FloatFilter defines model for FloatFilter. +// Valid indicates whether the value is a known member of the FindPatientsParamsWorkspaceIdType enum. +func (e FindPatientsParamsWorkspaceIdType) Valid() bool { + switch e { + case FindPatientsParamsWorkspaceIdTypeClinicId: + return true + case FindPatientsParamsWorkspaceIdTypeEhrSourceId: + return true + default: + return false + } +} + +// FloatFilter Example: >=5.5 type FloatFilter = string -// IntFilter defines model for IntFilter. +// IntFilter Example: >=7 type IntFilter = string // UpdateSuppressedNotifications defines model for UpdateSuppressedNotifications. @@ -776,6 +2272,7 @@ type UpdateSuppressedNotifications struct { // UpdateTier defines model for UpdateTier. type UpdateTier struct { + // Tier Example: tier0100 Tier TierV1 `json:"tier"` } @@ -794,146 +2291,242 @@ type AssociateClinicianToUserV1 struct { // BgmPeriodV1 Summary of a specific BGM time period (currently: 1d, 7d, 14d, 30d) type BgmPeriodV1 struct { // AverageDailyRecords Average daily readings + // + // Example: 3.5 AverageDailyRecords *float64 `json:"averageDailyRecords,omitempty"` // AverageDailyRecordsDelta Difference between the averageDailyRecords in this period and version in the opposite offset + // + // Example: 2.5 AverageDailyRecordsDelta *float64 `json:"averageDailyRecordsDelta,omitempty"` // AverageGlucoseMmol Average Glucose of records in this period + // + // Example: 5.5 AverageGlucoseMmol *float64 `json:"averageGlucoseMmol,omitempty"` // AverageGlucoseMmolDelta Difference between the averageGlucose in this period and the other offset version - AverageGlucoseMmolDelta *float64 `json:"averageGlucoseMmolDelta,omitempty"` - CoefficientOfVariation *float64 `json:"coefficientOfVariation,omitempty"` + // + // Example: 2.5 + AverageGlucoseMmolDelta *float64 `json:"averageGlucoseMmolDelta,omitempty"` + + // CoefficientOfVariation Example: 2.5 + CoefficientOfVariation *float64 `json:"coefficientOfVariation,omitempty"` + + // CoefficientOfVariationDelta Example: 2.5 CoefficientOfVariationDelta *float64 `json:"coefficientOfVariationDelta,omitempty"` - DaysWithData int `json:"daysWithData"` - DaysWithDataDelta int `json:"daysWithDataDelta"` - HasAverageDailyRecords bool `json:"hasAverageDailyRecords"` - HasAverageGlucoseMmol bool `json:"hasAverageGlucoseMmol"` - HasTimeInAnyHighPercent bool `json:"hasTimeInAnyHighPercent"` - HasTimeInAnyHighRecords bool `json:"hasTimeInAnyHighRecords"` - HasTimeInAnyLowPercent bool `json:"hasTimeInAnyLowPercent"` - HasTimeInAnyLowRecords bool `json:"hasTimeInAnyLowRecords"` - HasTimeInExtremeHighPercent bool `json:"hasTimeInExtremeHighPercent"` - HasTimeInExtremeHighRecords bool `json:"hasTimeInExtremeHighRecords"` - HasTimeInHighPercent bool `json:"hasTimeInHighPercent"` - HasTimeInHighRecords bool `json:"hasTimeInHighRecords"` - HasTimeInLowPercent bool `json:"hasTimeInLowPercent"` - HasTimeInLowRecords bool `json:"hasTimeInLowRecords"` - HasTimeInTargetPercent bool `json:"hasTimeInTargetPercent"` - HasTimeInTargetRecords bool `json:"hasTimeInTargetRecords"` - HasTimeInVeryHighPercent bool `json:"hasTimeInVeryHighPercent"` - HasTimeInVeryHighRecords bool `json:"hasTimeInVeryHighRecords"` - HasTimeInVeryLowPercent bool `json:"hasTimeInVeryLowPercent"` - HasTimeInVeryLowRecords bool `json:"hasTimeInVeryLowRecords"` - HasTotalRecords bool `json:"hasTotalRecords"` - Max float64 `json:"max"` - MaxDelta float64 `json:"maxDelta"` - Min float64 `json:"min"` - MinDelta float64 `json:"minDelta"` - StandardDeviation *float64 `json:"standardDeviation,omitempty"` - StandardDeviationDelta *float64 `json:"standardDeviationDelta,omitempty"` + + // DaysWithData Example: 2 + DaysWithData int `json:"daysWithData"` + + // DaysWithDataDelta Example: 2 + DaysWithDataDelta int `json:"daysWithDataDelta"` + HasAverageDailyRecords bool `json:"hasAverageDailyRecords"` + HasAverageGlucoseMmol bool `json:"hasAverageGlucoseMmol"` + HasTimeInAnyHighPercent bool `json:"hasTimeInAnyHighPercent"` + HasTimeInAnyHighRecords bool `json:"hasTimeInAnyHighRecords"` + HasTimeInAnyLowPercent bool `json:"hasTimeInAnyLowPercent"` + HasTimeInAnyLowRecords bool `json:"hasTimeInAnyLowRecords"` + HasTimeInExtremeHighPercent bool `json:"hasTimeInExtremeHighPercent"` + HasTimeInExtremeHighRecords bool `json:"hasTimeInExtremeHighRecords"` + HasTimeInHighPercent bool `json:"hasTimeInHighPercent"` + HasTimeInHighRecords bool `json:"hasTimeInHighRecords"` + HasTimeInLowPercent bool `json:"hasTimeInLowPercent"` + HasTimeInLowRecords bool `json:"hasTimeInLowRecords"` + HasTimeInTargetPercent bool `json:"hasTimeInTargetPercent"` + HasTimeInTargetRecords bool `json:"hasTimeInTargetRecords"` + HasTimeInVeryHighPercent bool `json:"hasTimeInVeryHighPercent"` + HasTimeInVeryHighRecords bool `json:"hasTimeInVeryHighRecords"` + HasTimeInVeryLowPercent bool `json:"hasTimeInVeryLowPercent"` + HasTimeInVeryLowRecords bool `json:"hasTimeInVeryLowRecords"` + HasTotalRecords bool `json:"hasTotalRecords"` + + // Max Example: 8 + Max float64 `json:"max"` + + // MaxDelta Example: 8 + MaxDelta float64 `json:"maxDelta"` + + // Min Example: 3 + Min float64 `json:"min"` + + // MinDelta Example: 3 + MinDelta float64 `json:"minDelta"` + + // StandardDeviation Example: 2.5 + StandardDeviation *float64 `json:"standardDeviation,omitempty"` + + // StandardDeviationDelta Example: 2.5 + StandardDeviationDelta *float64 `json:"standardDeviationDelta,omitempty"` // TimeInAnyHighPercent Percentage of time spent in Any high glucose range + // + // Example: 0.35 TimeInAnyHighPercent *float64 `json:"timeInAnyHighPercent,omitempty"` // TimeInAnyHighPercentDelta Difference between the timeInAnyHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInAnyHighPercentDelta *float64 `json:"timeInAnyHighPercentDelta,omitempty"` // TimeInAnyHighRecords Counter of records in Any high glucose range + // + // Example: 10 TimeInAnyHighRecords *int `json:"timeInAnyHighRecords,omitempty"` // TimeInAnyHighRecordsDelta Difference between the timeInAnyHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInAnyHighRecordsDelta *int `json:"timeInAnyHighRecordsDelta,omitempty"` // TimeInAnyLowPercent Percentage of time spent in Any low glucose range + // + // Example: 0.35 TimeInAnyLowPercent *float64 `json:"timeInAnyLowPercent,omitempty"` // TimeInAnyLowPercentDelta Difference between the timeInAnyLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInAnyLowPercentDelta *float64 `json:"timeInAnyLowPercentDelta,omitempty"` // TimeInAnyLowRecords Counter of records in Any low glucose range + // + // Example: 10 TimeInAnyLowRecords *int `json:"timeInAnyLowRecords,omitempty"` // TimeInAnyLowRecordsDelta Difference between the timeInAnyLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInAnyLowRecordsDelta *int `json:"timeInAnyLowRecordsDelta,omitempty"` // TimeInExtremeHighPercent Percentage of time spent in extreme high glucose range + // + // Example: 0.35 TimeInExtremeHighPercent *float64 `json:"timeInExtremeHighPercent,omitempty"` // TimeInExtremeHighPercentDelta Difference between the timeInExtremeHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInExtremeHighPercentDelta *float64 `json:"timeInExtremeHighPercentDelta,omitempty"` // TimeInExtremeHighRecords Counter of records in extreme high glucose range + // + // Example: 10 TimeInExtremeHighRecords *int `json:"timeInExtremeHighRecords,omitempty"` // TimeInExtremeHighRecordsDelta Difference between the timeInExtremeHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInExtremeHighRecordsDelta *int `json:"timeInExtremeHighRecordsDelta,omitempty"` // TimeInHighPercent Percentage of time spent in high glucose range + // + // Example: 0.35 TimeInHighPercent *float64 `json:"timeInHighPercent,omitempty"` // TimeInHighPercentDelta Difference between the timeInHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInHighPercentDelta *float64 `json:"timeInHighPercentDelta,omitempty"` // TimeInHighRecords Counter of records in high glucose range + // + // Example: 10 TimeInHighRecords *int `json:"timeInHighRecords,omitempty"` // TimeInHighRecordsDelta Difference between the timeInHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInHighRecordsDelta *int `json:"timeInHighRecordsDelta,omitempty"` // TimeInLowPercent Percentage of time spent in low glucose range + // + // Example: 0.35 TimeInLowPercent *float64 `json:"timeInLowPercent,omitempty"` // TimeInLowPercentDelta Difference between the timeInLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInLowPercentDelta *float64 `json:"timeInLowPercentDelta,omitempty"` // TimeInLowRecords Counter of records in low glucose range + // + // Example: 10 TimeInLowRecords *int `json:"timeInLowRecords,omitempty"` // TimeInLowRecordsDelta Difference between the timeInLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInLowRecordsDelta *int `json:"timeInLowRecordsDelta,omitempty"` // TimeInTargetPercent Percentage of time spent in target glucose range + // + // Example: 0.35 TimeInTargetPercent *float64 `json:"timeInTargetPercent,omitempty"` // TimeInTargetPercentDelta Difference between the timeInTargetPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInTargetPercentDelta *float64 `json:"timeInTargetPercentDelta,omitempty"` // TimeInTargetRecords Counter of records in target glucose range + // + // Example: 10 TimeInTargetRecords *int `json:"timeInTargetRecords,omitempty"` // TimeInTargetRecordsDelta Difference between the timeInTargetRecords in this period and version in the opposite offset + // + // Example: 5 TimeInTargetRecordsDelta *int `json:"timeInTargetRecordsDelta,omitempty"` // TimeInVeryHighPercent Percentage of time spent in very high glucose range + // + // Example: 0.35 TimeInVeryHighPercent *float64 `json:"timeInVeryHighPercent,omitempty"` // TimeInVeryHighPercentDelta Difference between the timeInVeryHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInVeryHighPercentDelta *float64 `json:"timeInVeryHighPercentDelta,omitempty"` // TimeInVeryHighRecords Counter of records in very high glucose range + // + // Example: 10 TimeInVeryHighRecords *int `json:"timeInVeryHighRecords,omitempty"` // TimeInVeryHighRecordsDelta Difference between the timeInVeryHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInVeryHighRecordsDelta *int `json:"timeInVeryHighRecordsDelta,omitempty"` // TimeInVeryLowPercent Percentage of time spent in very low glucose range + // + // Example: 0.35 TimeInVeryLowPercent *float64 `json:"timeInVeryLowPercent,omitempty"` // TimeInVeryLowPercentDelta Difference between the timeInVeryLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInVeryLowPercentDelta *float64 `json:"timeInVeryLowPercentDelta,omitempty"` // TimeInVeryLowRecords Counter of records in very low glucose range + // + // Example: 10 TimeInVeryLowRecords *int `json:"timeInVeryLowRecords,omitempty"` // TimeInVeryLowRecordsDelta Difference between the timeInVeryLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInVeryLowRecordsDelta *int `json:"timeInVeryLowRecordsDelta,omitempty"` // TotalRecords Counter of records + // + // Example: 10 TotalRecords *int `json:"totalRecords,omitempty"` // TotalRecordsDelta Difference between the totalRecords in this period and version in the opposite offset + // + // Example: 10 TotalRecordsDelta *int `json:"totalRecordsDelta,omitempty"` } @@ -949,6 +2542,8 @@ type BgmStatsV1 struct { Dates SummaryDatesV1 `json:"dates,omitempty"` // Id Summary Unique Identifier + // + // Example: 2fe2488217ee43e1b2e83c2f Id *SummaryIdV1 `json:"id,omitempty"` // Periods A map to each supported BGM summary period @@ -958,25 +2553,45 @@ type BgmStatsV1 struct { // CgmPeriodV1 Summary of a specific CGM time period (currently: 1d, 7d, 14d, 30d) type CgmPeriodV1 struct { // AverageDailyRecords Average daily readings + // + // Example: 3.5 AverageDailyRecords *float64 `json:"averageDailyRecords,omitempty"` // AverageDailyRecordsDelta Difference between the averageDailyRecords in this period and version in the opposite offset + // + // Example: 2.5 AverageDailyRecordsDelta *float64 `json:"averageDailyRecordsDelta,omitempty"` // AverageGlucoseMmol Average Glucose of records in this period + // + // Example: 5.5 AverageGlucoseMmol *float64 `json:"averageGlucoseMmol,omitempty"` // AverageGlucoseMmolDelta Difference between the averageGlucose in this period and the other offset version - AverageGlucoseMmolDelta *float64 `json:"averageGlucoseMmolDelta,omitempty"` - CoefficientOfVariation float64 `json:"coefficientOfVariation"` - CoefficientOfVariationDelta float64 `json:"coefficientOfVariationDelta"` - DaysWithData int `json:"daysWithData"` - DaysWithDataDelta int `json:"daysWithDataDelta"` + // + // Example: 2.5 + AverageGlucoseMmolDelta *float64 `json:"averageGlucoseMmolDelta,omitempty"` + + // CoefficientOfVariation Example: 2.5 + CoefficientOfVariation float64 `json:"coefficientOfVariation"` + + // CoefficientOfVariationDelta Example: 2.5 + CoefficientOfVariationDelta float64 `json:"coefficientOfVariationDelta"` + + // DaysWithData Example: 2 + DaysWithData int `json:"daysWithData"` + + // DaysWithDataDelta Example: 2 + DaysWithDataDelta int `json:"daysWithDataDelta"` // GlucoseManagementIndicator A derived value which emulates A1C + // + // Example: 7.5 GlucoseManagementIndicator *float64 `json:"glucoseManagementIndicator,omitempty"` // GlucoseManagementIndicatorDelta Difference between the glucoseManagementIndicator in this period and the other offset version + // + // Example: 7.5 GlucoseManagementIndicatorDelta *float64 `json:"glucoseManagementIndicatorDelta,omitempty"` HasAverageDailyRecords bool `json:"hasAverageDailyRecords"` HasAverageGlucoseMmol bool `json:"hasAverageGlucoseMmol"` @@ -1009,181 +2624,309 @@ type CgmPeriodV1 struct { HasTimeInVeryLowPercent bool `json:"hasTimeInVeryLowPercent"` HasTimeInVeryLowRecords bool `json:"hasTimeInVeryLowRecords"` HasTotalRecords bool `json:"hasTotalRecords"` - HoursWithData int `json:"hoursWithData"` - HoursWithDataDelta int `json:"hoursWithDataDelta"` - Max float64 `json:"max"` - MaxDelta float64 `json:"maxDelta"` - Min float64 `json:"min"` - MinDelta float64 `json:"minDelta"` - StandardDeviation float64 `json:"standardDeviation"` - StandardDeviationDelta float64 `json:"standardDeviationDelta"` + + // HoursWithData Example: 2 + HoursWithData int `json:"hoursWithData"` + + // HoursWithDataDelta Example: 2 + HoursWithDataDelta int `json:"hoursWithDataDelta"` + + // Max Example: 8 + Max float64 `json:"max"` + + // MaxDelta Example: 8 + MaxDelta float64 `json:"maxDelta"` + + // Min Example: 3 + Min float64 `json:"min"` + + // MinDelta Example: 3 + MinDelta float64 `json:"minDelta"` + + // StandardDeviation Example: 2.5 + StandardDeviation float64 `json:"standardDeviation"` + + // StandardDeviationDelta Example: 2.5 + StandardDeviationDelta float64 `json:"standardDeviationDelta"` // TimeCGMUseMinutes Counter of minutes spent wearing a cgm + // + // Example: 200 TimeCGMUseMinutes *int `json:"timeCGMUseMinutes,omitempty"` // TimeCGMUseMinutesDelta Difference between the timeCGMUseMinutes in this period and version in the opposite offset + // + // Example: 5 TimeCGMUseMinutesDelta *int `json:"timeCGMUseMinutesDelta,omitempty"` // TimeCGMUsePercent Percentage of time spent wearing a cgm + // + // Example: 0.35 TimeCGMUsePercent *float64 `json:"timeCGMUsePercent,omitempty"` // TimeCGMUsePercentDelta Difference between the timeCGMUsePercent in this period and version in the opposite offset + // + // Example: 0.2 TimeCGMUsePercentDelta *float64 `json:"timeCGMUsePercentDelta,omitempty"` // TimeCGMUseRecords Counter of minutes spent wearing a cgm + // + // Example: 10 TimeCGMUseRecords *int `json:"timeCGMUseRecords,omitempty"` // TimeCGMUseRecordsDelta Difference between the timeCGMUseRecords in this period and version in the opposite offset + // + // Example: 5 TimeCGMUseRecordsDelta *int `json:"timeCGMUseRecordsDelta,omitempty"` // TimeInAnyHighMinutes Counter of minutes spent in Any high glucose range + // + // Example: 200 TimeInAnyHighMinutes *int `json:"timeInAnyHighMinutes,omitempty"` // TimeInAnyHighMinutesDelta Difference between the timeInAnyHighMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInAnyHighMinutesDelta *int `json:"timeInAnyHighMinutesDelta,omitempty"` // TimeInAnyHighPercent Percentage of time spent in Any high glucose range + // + // Example: 0.35 TimeInAnyHighPercent *float64 `json:"timeInAnyHighPercent,omitempty"` // TimeInAnyHighPercentDelta Difference between the timeInAnyHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInAnyHighPercentDelta *float64 `json:"timeInAnyHighPercentDelta,omitempty"` // TimeInAnyHighRecords Counter of records in Any high glucose range + // + // Example: 10 TimeInAnyHighRecords *int `json:"timeInAnyHighRecords,omitempty"` // TimeInAnyHighRecordsDelta Difference between the timeInAnyHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInAnyHighRecordsDelta *int `json:"timeInAnyHighRecordsDelta,omitempty"` // TimeInAnyLowMinutes Counter of minutes spent in Any low glucose range + // + // Example: 200 TimeInAnyLowMinutes *int `json:"timeInAnyLowMinutes,omitempty"` // TimeInAnyLowMinutesDelta Difference between the timeInAnyLowMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInAnyLowMinutesDelta *int `json:"timeInAnyLowMinutesDelta,omitempty"` // TimeInAnyLowPercent Percentage of time spent in Any low glucose range + // + // Example: 0.35 TimeInAnyLowPercent *float64 `json:"timeInAnyLowPercent,omitempty"` // TimeInAnyLowPercentDelta Difference between the timeInAnyLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInAnyLowPercentDelta *float64 `json:"timeInAnyLowPercentDelta,omitempty"` // TimeInAnyLowRecords Counter of records in Any low glucose range + // + // Example: 10 TimeInAnyLowRecords *int `json:"timeInAnyLowRecords,omitempty"` // TimeInAnyLowRecordsDelta Difference between the timeInAnyLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInAnyLowRecordsDelta *int `json:"timeInAnyLowRecordsDelta,omitempty"` // TimeInExtremeHighMinutes Counter of minutes spent in extreme high glucose range + // + // Example: 200 TimeInExtremeHighMinutes *int `json:"timeInExtremeHighMinutes,omitempty"` // TimeInExtremeHighMinutesDelta Difference between the timeInExtremeHighMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInExtremeHighMinutesDelta *int `json:"timeInExtremeHighMinutesDelta,omitempty"` // TimeInExtremeHighPercent Percentage of time spent in extreme high glucose range + // + // Example: 0.35 TimeInExtremeHighPercent *float64 `json:"timeInExtremeHighPercent,omitempty"` // TimeInExtremeHighPercentDelta Difference between the timeInExtremeHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInExtremeHighPercentDelta *float64 `json:"timeInExtremeHighPercentDelta,omitempty"` // TimeInExtremeHighRecords Counter of records in extreme high glucose range + // + // Example: 10 TimeInExtremeHighRecords *int `json:"timeInExtremeHighRecords,omitempty"` // TimeInExtremeHighRecordsDelta Difference between the timeInExtremeHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInExtremeHighRecordsDelta *int `json:"timeInExtremeHighRecordsDelta,omitempty"` // TimeInHighMinutes Counter of minutes spent in high glucose range + // + // Example: 200 TimeInHighMinutes *int `json:"timeInHighMinutes,omitempty"` // TimeInHighMinutesDelta Difference between the timeInHighMinutes in this period and version in the opposite offset + // + // Example: 20 TimeInHighMinutesDelta *int `json:"timeInHighMinutesDelta,omitempty"` // TimeInHighPercent Percentage of time spent in high glucose range + // + // Example: 0.35 TimeInHighPercent *float64 `json:"timeInHighPercent,omitempty"` // TimeInHighPercentDelta Difference between the timeInHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInHighPercentDelta *float64 `json:"timeInHighPercentDelta,omitempty"` // TimeInHighRecords Counter of records in high glucose range + // + // Example: 10 TimeInHighRecords *int `json:"timeInHighRecords,omitempty"` // TimeInHighRecordsDelta Difference between the timeInHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInHighRecordsDelta *int `json:"timeInHighRecordsDelta,omitempty"` // TimeInLowMinutes Counter of minutes spent in low glucose range + // + // Example: 200 TimeInLowMinutes *int `json:"timeInLowMinutes,omitempty"` // TimeInLowMinutesDelta Difference between the timeInLowMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInLowMinutesDelta *int `json:"timeInLowMinutesDelta,omitempty"` // TimeInLowPercent Percentage of time spent in low glucose range + // + // Example: 0.35 TimeInLowPercent *float64 `json:"timeInLowPercent,omitempty"` // TimeInLowPercentDelta Difference between the timeInLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInLowPercentDelta *float64 `json:"timeInLowPercentDelta,omitempty"` // TimeInLowRecords Counter of records in low glucose range + // + // Example: 10 TimeInLowRecords *int `json:"timeInLowRecords,omitempty"` // TimeInLowRecordsDelta Difference between the timeInLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInLowRecordsDelta *int `json:"timeInLowRecordsDelta,omitempty"` // TimeInTargetMinutes Counter of minutes spent in target glucose range + // + // Example: 200 TimeInTargetMinutes *int `json:"timeInTargetMinutes,omitempty"` // TimeInTargetMinutesDelta Difference between the timeInTargetMinutes in this period and version in the opposite offset + // + // Example: 20 TimeInTargetMinutesDelta *int `json:"timeInTargetMinutesDelta,omitempty"` // TimeInTargetPercent Percentage of time spent in target glucose range + // + // Example: 0.35 TimeInTargetPercent *float64 `json:"timeInTargetPercent,omitempty"` // TimeInTargetPercentDelta Difference between the timeInTargetPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInTargetPercentDelta *float64 `json:"timeInTargetPercentDelta,omitempty"` // TimeInTargetRecords Counter of records in target glucose range + // + // Example: 10 TimeInTargetRecords *int `json:"timeInTargetRecords,omitempty"` // TimeInTargetRecordsDelta Difference between the timeInTargetRecords in this period and version in the opposite offset + // + // Example: 5 TimeInTargetRecordsDelta *int `json:"timeInTargetRecordsDelta,omitempty"` // TimeInVeryHighMinutes Counter of minutes spent in very high glucose range + // + // Example: 200 TimeInVeryHighMinutes *int `json:"timeInVeryHighMinutes,omitempty"` // TimeInVeryHighMinutesDelta Difference between the timeInVeryHighMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInVeryHighMinutesDelta *int `json:"timeInVeryHighMinutesDelta,omitempty"` // TimeInVeryHighPercent Percentage of time spent in very high glucose range + // + // Example: 0.35 TimeInVeryHighPercent *float64 `json:"timeInVeryHighPercent,omitempty"` // TimeInVeryHighPercentDelta Difference between the timeInVeryHighPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInVeryHighPercentDelta *float64 `json:"timeInVeryHighPercentDelta,omitempty"` // TimeInVeryHighRecords Counter of records in very high glucose range + // + // Example: 10 TimeInVeryHighRecords *int `json:"timeInVeryHighRecords,omitempty"` // TimeInVeryHighRecordsDelta Difference between the timeInVeryHighRecords in this period and version in the opposite offset + // + // Example: 5 TimeInVeryHighRecordsDelta *int `json:"timeInVeryHighRecordsDelta,omitempty"` // TimeInVeryLowMinutes Counter of minutes spent in very low glucose range + // + // Example: 200 TimeInVeryLowMinutes *int `json:"timeInVeryLowMinutes,omitempty"` // TimeInVeryLowMinutesDelta Difference between the timeInVeryLowMinutes in this period and version in the opposite offset + // + // Example: 5 TimeInVeryLowMinutesDelta *int `json:"timeInVeryLowMinutesDelta,omitempty"` // TimeInVeryLowPercent Percentage of time spent in very low glucose range + // + // Example: 0.35 TimeInVeryLowPercent *float64 `json:"timeInVeryLowPercent,omitempty"` // TimeInVeryLowPercentDelta Difference between the timeInVeryLowPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInVeryLowPercentDelta *float64 `json:"timeInVeryLowPercentDelta,omitempty"` // TimeInVeryLowRecords Counter of records in very low glucose range + // + // Example: 10 TimeInVeryLowRecords *int `json:"timeInVeryLowRecords,omitempty"` // TimeInVeryLowRecordsDelta Difference between the timeInVeryLowRecords in this period and version in the opposite offset + // + // Example: 5 TimeInVeryLowRecordsDelta *int `json:"timeInVeryLowRecordsDelta,omitempty"` // TotalRecords Counter of records + // + // Example: 10 TotalRecords *int `json:"totalRecords,omitempty"` // TotalRecordsDelta Difference between the totalRecords in this period and version in the opposite offset + // + // Example: 10 TotalRecordsDelta *int `json:"totalRecordsDelta,omitempty"` } @@ -1199,6 +2942,8 @@ type CgmStatsV1 struct { Dates SummaryDatesV1 `json:"dates,omitempty"` // Id Summary Unique Identifier + // + // Example: 2fe2488217ee43e1b2e83c2f Id *SummaryIdV1 `json:"id,omitempty"` // Periods A map to each supported CGM summary period @@ -1206,6 +2951,8 @@ type CgmStatsV1 struct { } // CityV1 City name. +// +// Example: Palo Alto type CityV1 = string // ClinicV1 Clinic @@ -1215,15 +2962,21 @@ type ClinicV1 struct { CanMigrate *bool `json:"canMigrate,omitempty"` // City City name. + // + // Example: Palo Alto City *CityV1 `json:"city,omitempty"` ClinicSize *ClinicV1ClinicSize `json:"clinicSize,omitempty"` ClinicType *ClinicV1ClinicType `json:"clinicType,omitempty"` // Country Country name. + // + // Example: USA Country *CountryV1 `json:"country,omitempty"` CreatedTime *time.Time `json:"createdTime,omitempty"` // Id Clinic identifier. + // + // Example: 2fe2488217ee43e1b2e83c2f Id *ClinicIdV1 `json:"id,omitempty"` LastDeletedPatientTag *PatientTagV1 `json:"lastDeletedPatientTag,omitempty"` @@ -1235,6 +2988,8 @@ type ClinicV1 struct { PhoneNumbers *PhoneNumbersV1 `json:"phoneNumbers,omitempty"` // PostalCode Postal code. In the U.S., typically the zip code such as `94301` or `94301-1704`. + // + // Example: 94301 PostalCode *PostalCodeV1 `json:"postalCode,omitempty"` PreferredBgUnits ClinicV1PreferredBgUnits `json:"preferredBgUnits"` @@ -1243,13 +2998,19 @@ type ClinicV1 struct { Sites []SiteV1 `json:"sites,omitempty"` // State State or province. In the U.S., typically something like `CA` or `California`. + // + // Example: CA State *StateV1 `json:"state,omitempty"` SuppressedNotifications *SuppressedNotificationsV1 `json:"suppressedNotifications,omitempty"` - Tier *string `json:"tier,omitempty"` - TierDescription *string `json:"tierDescription,omitempty"` - Timezone *ClinicTimezoneV1 `json:"timezone,omitempty"` - UpdatedTime *time.Time `json:"updatedTime,omitempty"` - Website *string `json:"website,omitempty"` + + // Tier Example: tier0100 + Tier *string `json:"tier,omitempty"` + + // TierDescription Example: Free + TierDescription *string `json:"tierDescription,omitempty"` + Timezone *ClinicTimezoneV1 `json:"timezone,omitempty"` + UpdatedTime *time.Time `json:"updatedTime,omitempty"` + Website *string `json:"website,omitempty"` } // ClinicV1ClinicSize defines model for ClinicV1.ClinicSize. @@ -1262,6 +3023,8 @@ type ClinicV1ClinicType string type ClinicV1PreferredBgUnits string // ClinicIdV1 Clinic identifier. +// +// Example: 2fe2488217ee43e1b2e83c2f type ClinicIdV1 = string // ClinicTimezoneV1 defines model for clinicTimezone.v1. @@ -1306,13 +3069,19 @@ type CliniciansV1 = []ClinicianV1 type ClinicsV1 = []ClinicV1 // CountryV1 Country name. +// +// Example: USA type CountryV1 = string // CreatePatientV1 defines model for createPatient.v1. type CreatePatientV1 struct { - AttestationSubmitted *bool `json:"attestationSubmitted,omitempty"` - BirthDate *openapi_types.Date `json:"birthDate,omitempty"` - DiagnosisType *DiagnosisTypeV1 `json:"diagnosisType,omitempty"` + AttestationSubmitted *bool `json:"attestationSubmitted,omitempty"` + + // BirthDate Example: 2012-08-30 + BirthDate *openapi_types.Date `json:"birthDate,omitempty"` + + // DiagnosisType Example: type1 + DiagnosisType *DiagnosisTypeV1 `json:"diagnosisType,omitempty"` // FullName The full name of the patient FullName *string `json:"fullName,omitempty"` @@ -1326,7 +3095,7 @@ type CreatePatientV1 struct { Mrn *string `json:"mrn,omitempty"` Permissions *PatientPermissionsV1 `json:"permissions,omitempty"` Sites []SiteV1 `json:"sites,omitzero"` - Tags *PatientTagIdsV1 `json:"tags"` + Tags *PatientTagIdsV1 `json:"tags,omitempty"` } // DataSourceV1 defines model for dataSource.v1. @@ -1335,27 +3104,39 @@ type DataSourceV1 struct { DataSourceId *string `json:"dataSourceId,omitempty"` // ExpirationTime [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information + // + // Example: 2017-02-06T02:37:46Z ExpirationTime *DatetimeV1 `json:"expirationTime,omitempty"` // LatestDataTime [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information + // + // Example: 2017-02-06T02:37:46Z LatestDataTime *DatetimeV1 `json:"latestDataTime,omitempty"` // ModifiedTime [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information - ModifiedTime *DatetimeV1 `json:"modifiedTime,omitempty"` - ProviderName ProviderNameV1 `json:"providerName"` - State DataSourceV1State `json:"state"` + // + // Example: 2017-02-06T02:37:46Z + ModifiedTime *DatetimeV1 `json:"modifiedTime,omitempty"` + + // ProviderName Example: dexcom + ProviderName ProviderNameV1 `json:"providerName"` + + // State Example: connected + State DataSourceV1State `json:"state"` } -// DataSourceV1State defines model for DataSourceV1.State. +// DataSourceV1State Example: connected type DataSourceV1State string // DataSourcesV1 defines model for dataSources.v1. type DataSourcesV1 = []DataSourceV1 // DatetimeV1 [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information +// +// Example: 2017-02-06T02:37:46Z type DatetimeV1 = string -// DiagnosisTypeV1 defines model for diagnosisType.v1. +// DiagnosisTypeV1 Example: type1 type DiagnosisTypeV1 string // EhrDestinationsV1 defines model for ehrDestinations.v1. @@ -1468,13 +3249,18 @@ type EhrTagsSettingsV1 struct { // ErrorV1 defines model for error.v1. type ErrorV1 struct { - Code int `json:"code"` + // Code Example: 404 + Code int `json:"code"` + + // Message Example: Requested resources was not found Message string `json:"message"` } // GenerateMergeReportV1 defines model for generateMergeReport.v1. type GenerateMergeReportV1 struct { // SourceId Clinic identifier. + // + // Example: 2fe2488217ee43e1b2e83c2f SourceId *ClinicIdV1 `json:"sourceId,omitempty"` } @@ -1530,6 +3316,8 @@ type MembershipRestrictionsV1 struct { // MergeClinicV1 defines model for mergeClinic.v1. type MergeClinicV1 struct { // SourceId Clinic identifier. + // + // Example: 2fe2488217ee43e1b2e83c2f SourceId *ClinicIdV1 `json:"sourceId,omitempty"` } @@ -1582,18 +3370,25 @@ type NameV1 = string // ObjectIdV1 String representation of a resource id type ObjectIdV1 = string +// ObjectIdOrZeroV1 A resource id, or the underscore sentinel `_` meaning "records with none assigned". +type ObjectIdOrZeroV1 = string + // ObjectidV1 String representation of a resource id type ObjectidV1 = string // PatientV1 defines model for patient.v1. type PatientV1 struct { - AttestationSubmitted *bool `json:"attestationSubmitted,omitempty"` - BirthDate openapi_types.Date `json:"birthDate"` - ConnectionRequests *ProviderConnectionRequestsV1 `json:"connectionRequests,omitempty"` - CreatedTime *time.Time `json:"createdTime,omitempty"` - DataSources *[]DataSourceV1 `json:"dataSources"` - DiagnosisType *DiagnosisTypeV1 `json:"diagnosisType,omitempty"` - Email *string `json:"email,omitempty"` + AttestationSubmitted *bool `json:"attestationSubmitted,omitempty"` + + // BirthDate Example: 2012-08-30 + BirthDate openapi_types.Date `json:"birthDate"` + ConnectionRequests *ProviderConnectionRequestsV1 `json:"connectionRequests,omitempty"` + CreatedTime *time.Time `json:"createdTime,omitempty"` + DataSources *[]DataSourceV1 `json:"dataSources,omitempty"` + + // DiagnosisType Example: type1 + DiagnosisType *DiagnosisTypeV1 `json:"diagnosisType,omitempty"` + Email *string `json:"email,omitempty"` // FullName The full name of the patient FullName string `json:"fullName"` @@ -1606,14 +3401,22 @@ type PatientV1 struct { // Mrn The medical record number of the patient Mrn *string `json:"mrn,omitempty"` Permissions *PatientPermissionsV1 `json:"permissions,omitempty"` - Reviews []PatientReviewV1 `json:"reviews"` + Reviews []PatientReviewV1 `json:"reviews,omitempty"` Sites []SiteV1 `json:"sites,omitzero"` // Summary A summary of a patients recent data - Summary *PatientSummaryV1 `json:"summary,omitempty"` - Tags *PatientTagIdsV1 `json:"tags"` - TargetDevices *[]string `json:"targetDevices,omitempty"` - UpdatedTime *time.Time `json:"updatedTime,omitempty"` + Summary *PatientSummaryV1 `json:"summary,omitempty"` + Tags *PatientTagIdsV1 `json:"tags,omitempty"` + + // TargetDevices When uploading data, the user selects a target device (typically + // make and model) of their data source (i.e. meter or pump) from a + // list. This selection is used by the Tidepool Uploader to select the + // correct drivers for communicating with the data source. + // + // These values are stored in the user's profile so that they can be + // pre-selected on next use, thereby streamlining the process. + TargetDevices *TargetdevicesV1 `json:"targetDevices,omitempty"` + UpdatedTime *time.Time `json:"updatedTime,omitempty"` } // PatientClinicRelationshipV1 defines model for patientClinicRelationship.v1. @@ -1650,12 +3453,16 @@ type PatientCountV1 struct { // PatientCountLimitV1 defines model for patientCountLimit.v1. type PatientCountLimitV1 struct { // EndDate [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information + // + // Example: 2017-02-06T02:37:46Z EndDate *DatetimeV1 `json:"endDate,omitempty"` // Plan The plan-based patient count limit Plan int `json:"plan"` // StartDate [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) / [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamp _with_ timezone information + // + // Example: 2017-02-06T02:37:46Z StartDate *DatetimeV1 `json:"startDate,omitempty"` } @@ -1724,7 +3531,7 @@ type PatientsResponseV1 struct { Meta *MetaV1 `json:"meta,omitempty"` } -// PhoneNumberV1 defines model for phoneNumber.v1. +// PhoneNumberV1 Example: {"number":"+1 (555) 555-1212","type":"main"} type PhoneNumberV1 struct { Number string `json:"number"` Type *string `json:"type,omitempty"` @@ -1734,6 +3541,8 @@ type PhoneNumberV1 struct { type PhoneNumbersV1 = []PhoneNumberV1 // PostalCodeV1 Postal code. In the U.S., typically the zip code such as `94301` or `94301-1704`. +// +// Example: 94301 type PostalCodeV1 = string // ProviderConnectionRequestV1 defines model for providerConnectionRequest.v1. @@ -1752,7 +3561,7 @@ type ProviderConnectionRequestsV1 struct { // ProviderIdV1 defines model for providerId.v1. type ProviderIdV1 string -// ProviderNameV1 defines model for providerName.v1. +// ProviderNameV1 Example: dexcom type ProviderNameV1 = string // ScheduledReportsV1 Scheduled Report Settings @@ -1798,10 +3607,15 @@ type SiteCreationV1 struct { // SiteIdV1 defines model for siteId.v1. type SiteIdV1 = string +// SiteIdsV1 defines model for siteIds.v1. +type SiteIdsV1 = []SiteIdV1 + // SiteNameV1 The site's name. type SiteNameV1 = string // StateV1 State or province. In the U.S., typically something like `CA` or `California`. +// +// Example: CA type StateV1 = string // StreetAddressV1 Street address. @@ -1857,6 +3671,8 @@ type SummaryDatesV1 struct { } // SummaryIdV1 Summary Unique Identifier +// +// Example: 2fe2488217ee43e1b2e83c2f type SummaryIdV1 = string // SuppressedNotificationsV1 defines model for suppressedNotifications.v1. @@ -1864,9 +3680,20 @@ type SuppressedNotificationsV1 struct { PatientClinicInvitation *bool `json:"patientClinicInvitation,omitempty"` } +// TargetdevicesV1 When uploading data, the user selects a target device (typically +// make and model) of their data source (i.e. meter or pump) from a +// list. This selection is used by the Tidepool Uploader to select the +// correct drivers for communicating with the data source. +// +// These values are stored in the user's profile so that they can be +// pre-selected on next use, thereby streamlining the process. +type TargetdevicesV1 = []string + // TideConfigV1 defines model for tideConfig.v1. type TideConfigV1 struct { // ClinicId Clinic identifier. + // + // Example: 2fe2488217ee43e1b2e83c2f ClinicId *ClinicIdV1 `json:"clinicId,omitempty"` // ExtremeHighGlucoseThreshold Minimum inclusive threshold in mmol/L for categorizing if a glucose value is extremely high. Not defined by the AACE. @@ -1881,10 +3708,13 @@ type TideConfigV1 struct { // LowGlucoseThreshold Maximum exclusive threshold in mmol/L for categorizing if a glucose value is low as established by the AACE. LowGlucoseThreshold float64 `json:"lowGlucoseThreshold"` - Period string `json:"period"` + + // Period Example: 7d + Period string `json:"period"` // SchemaVersion TIDE schema version SchemaVersion int `json:"schemaVersion"` + Sites *SiteIdsV1 `json:"sites,omitempty,omitzero"` Tags *PatientTagIdsV1 `json:"tags"` // VeryHighGlucoseThreshold Minimum exclusive threshold in mmol/L for categorizing if a glucose value is very high as established by the AACE. @@ -1896,19 +3726,34 @@ type TideConfigV1 struct { // TideFiltersV1 Visual representation of filtered categories selected type TideFiltersV1 struct { + // DropInTimeInTargetPercent Example: >0.5 DropInTimeInTargetPercent *string `json:"dropInTimeInTargetPercent,omitempty"` - TimeCGMUsePercent *string `json:"timeCGMUsePercent,omitempty"` - TimeInAnyLowPercent *string `json:"timeInAnyLowPercent,omitempty"` - TimeInExtremeHighPercent *string `json:"timeInExtremeHighPercent,omitempty"` - TimeInHighPercent *string `json:"timeInHighPercent,omitempty"` - TimeInTargetPercent *string `json:"timeInTargetPercent,omitempty"` - TimeInVeryHighPercent *string `json:"timeInVeryHighPercent,omitempty"` - TimeInVeryLowPercent *string `json:"timeInVeryLowPercent,omitempty"` + + // TimeCGMUsePercent Example: >0.5 + TimeCGMUsePercent *string `json:"timeCGMUsePercent,omitempty"` + + // TimeInAnyLowPercent Example: >0.5 + TimeInAnyLowPercent *string `json:"timeInAnyLowPercent,omitempty"` + + // TimeInExtremeHighPercent Example: >0.5 + TimeInExtremeHighPercent *string `json:"timeInExtremeHighPercent,omitempty"` + + // TimeInHighPercent Example: >0.5 + TimeInHighPercent *string `json:"timeInHighPercent,omitempty"` + + // TimeInTargetPercent Example: >0.5 + TimeInTargetPercent *string `json:"timeInTargetPercent,omitempty"` + + // TimeInVeryHighPercent Example: >0.5 + TimeInVeryHighPercent *string `json:"timeInVeryHighPercent,omitempty"` + + // TimeInVeryLowPercent Example: >0.5 + TimeInVeryLowPercent *string `json:"timeInVeryLowPercent,omitempty"` } // TidePatientV1 defines model for tidePatient.v1. type TidePatientV1 struct { - DataSources *[]DataSourceV1 `json:"dataSources"` + DataSources *[]DataSourceV1 `json:"dataSources,omitempty"` Email *string `json:"email,omitempty"` // FullName The full name of the patient @@ -1916,7 +3761,7 @@ type TidePatientV1 struct { // Id String representation of a Tidepool User ID. Old style IDs are 10-digit strings consisting of only hexadeximcal digits. New style IDs are 36-digit [UUID v4](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)) Id *Tidepooluserid `json:"id,omitempty"` - Reviews []PatientReviewV1 `json:"reviews"` + Reviews []PatientReviewV1 `json:"reviews,omitempty"` Tags *PatientTagIdsV1 `json:"tags"` } @@ -1929,44 +3774,70 @@ type TideResponseV1 struct { // TideResultPatientV1 defines model for tideResultPatient.v1. type TideResultPatientV1 struct { // AverageGlucoseMmol Average Glucose of records in this period + // + // Example: 5.5 AverageGlucoseMmol *float64 `json:"averageGlucoseMmol,omitempty"` // GlucoseManagementIndicator A derived value which emulates A1C + // + // Example: 7.5 GlucoseManagementIndicator *float64 `json:"glucoseManagementIndicator,omitempty"` LastData *time.Time `json:"lastData,omitempty"` Patient TidePatientV1 `json:"patient"` // TimeCGMUseMinutes Counter of minutes spent wearing a cgm + // + // Example: 200 TimeCGMUseMinutes *int `json:"timeCGMUseMinutes,omitempty"` // TimeCGMUsePercent Percentage of time spent wearing a cgm + // + // Example: 0.35 TimeCGMUsePercent *float64 `json:"timeCGMUsePercent,omitempty"` // TimeInAnyHighPercent Percentage of time spent in any high glucose range + // + // Example: 0.35 TimeInAnyHighPercent *float64 `json:"timeInAnyHighPercent,omitempty"` // TimeInAnyLowPercent Percentage of time spent in any low glucose range + // + // Example: 0.35 TimeInAnyLowPercent *float64 `json:"timeInAnyLowPercent,omitempty"` // TimeInExtremeHighPercent Percentage of time spent in extreme high glucose range + // + // Example: 0.35 TimeInExtremeHighPercent *float64 `json:"timeInExtremeHighPercent,omitempty"` // TimeInHighPercent Percentage of time spent in high glucose range + // + // Example: 0.35 TimeInHighPercent *float64 `json:"timeInHighPercent,omitempty"` // TimeInLowPercent Percentage of time spent in low glucose range + // + // Example: 0.35 TimeInLowPercent *float64 `json:"timeInLowPercent,omitempty"` // TimeInTargetPercent Percentage of time spent in target glucose range + // + // Example: 0.35 TimeInTargetPercent *float64 `json:"timeInTargetPercent,omitempty"` // TimeInTargetPercentDelta Difference between the timeInTargetPercent in this period and version in the opposite offset + // + // Example: 0.2 TimeInTargetPercentDelta *float64 `json:"timeInTargetPercentDelta,omitempty"` // TimeInVeryHighPercent Percentage of time spent in very high glucose range + // + // Example: 0.35 TimeInVeryHighPercent *float64 `json:"timeInVeryHighPercent,omitempty"` // TimeInVeryLowPercent Percentage of time spent in very low glucose range + // + // Example: 0.35 TimeInVeryLowPercent *float64 `json:"timeInVeryLowPercent,omitempty"` } @@ -1979,7 +3850,7 @@ type TidepoolUserIdsV1 = []Tidepooluserid // Tidepooluserid String representation of a Tidepool User ID. Old style IDs are 10-digit strings consisting of only hexadeximcal digits. New style IDs are 36-digit [UUID v4](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)) type Tidepooluserid = string -// TierV1 defines model for tier.v1. +// TierV1 Example: tier0100 type TierV1 string // TriggerMigrationV1 defines model for triggerMigration.v1. @@ -2044,6 +3915,8 @@ type SiteId = ObjectIdV1 type Sort = string // SummaryId Summary Unique Identifier +// +// Example: 2fe2488217ee43e1b2e83c2f type SummaryId = SummaryIdV1 // UserId String representation of a Tidepool User ID. Old style IDs are 10-digit strings consisting of only hexadeximcal digits. New style IDs are 36-digit [UUID v4](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)) @@ -2460,11 +4333,11 @@ type ListPatientsParams struct { // BgmLastDataTo Exclusive BgmLastDataTo *time.Time `form:"bgm.lastDataTo,omitempty" json:"bgm.lastDataTo,omitempty"` - // Tags Comma-separated list of patient tag IDs - Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"` + // Tags Comma-separated list of patient tag IDs. The single value `_` filters for patients without any tags assigned; combining `_` with tag IDs is rejected. + Tags *[]ObjectIdOrZeroV1 `form:"tags,omitempty" json:"tags,omitempty"` - // Sites Comma-separated list of clinic site IDs - Sites *[]string `form:"sites,omitempty" json:"sites,omitempty"` + // Sites Comma-separated list of clinic site IDs. The single value `_` filters for patients without any sites assigned; combining `_` with site IDs is rejected. + Sites *[]ObjectIdOrZeroV1 `form:"sites,omitempty" json:"sites,omitempty"` // OmitNonStandardRanges Whether patients whose glycemic ranges selection is *not* // the ADA standard ranges (e.g. as used by the TIDE report) @@ -2484,10 +4357,13 @@ type TideReportParams struct { LastDataCutoff time.Time `form:"lastDataCutoff" json:"lastDataCutoff"` // Categories Comma-separated list of TIDE report categories to return in queried order. If omitted or empty, the default TIDE categories will be returned - see example. - Categories []TideReportParamsCategories `form:"categories,omitempty" json:"categories,omitempty"` + Categories []TideReportParamsCategories `form:"categories,omitempty" json:"categories,omitempty,omitzero"` // ExcludeNoData If true, then exclude / omit patients with no data in the TIDE report. ExcludeNoData bool `form:"excludeNoData,omitempty" json:"excludeNoData,omitempty"` + + // Sites Comma-separated list of clinic site IDs. If provided, only patients assigned to at least one of the given sites are included in the report. An empty value is ignored, as if the parameter were omitted. + Sites *[]ObjectIdV1 `form:"sites,omitempty" json:"sites,omitempty,omitzero"` } // TideReportParamsCategories defines parameters for TideReport. diff --git a/go.mod b/go.mod index 7a053a014..6024980dd 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/tidepool-org/clinic -go 1.24.1 +go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.59.0 @@ -10,17 +10,17 @@ require ( github.com/dominikbraun/graph v0.23.0 github.com/eapache/queue v1.1.0 github.com/fatih/structs v1.1.0 - github.com/getkin/kin-openapi v0.132.0 + github.com/getkin/kin-openapi v0.142.0 github.com/golang-jwt/jwt/v4 v4.5.1 github.com/google/uuid v1.6.0 github.com/hashicorp/golang-lru v1.0.2 github.com/jaswdr/faker v1.19.1 github.com/kelseyhightower/envconfig v1.4.0 - github.com/labstack/echo/v4 v4.12.0 + github.com/labstack/echo/v4 v4.15.4 github.com/mitchellh/mapstructure v1.5.0 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 - github.com/oapi-codegen/echo-middleware v1.0.2 - github.com/oapi-codegen/runtime v1.1.2 + github.com/oapi-codegen/echo-middleware v1.1.0 + github.com/oapi-codegen/runtime v1.6.0 github.com/onsi/ginkgo/v2 v2.22.0 github.com/onsi/gomega v1.36.0 github.com/open-policy-agent/opa v0.70.0 @@ -35,7 +35,7 @@ require ( go.uber.org/mock v0.5.0 go.uber.org/zap v1.27.0 golang.org/x/oauth2 v0.24.0 - golang.org/x/text v0.22.0 + golang.org/x/text v0.40.0 ) require ( @@ -62,11 +62,9 @@ require ( github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/jsonpointer v1.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/flatbuffers v24.3.25+incompatible // indirect @@ -83,23 +81,20 @@ require ( github.com/jcmturner/gofork v1.7.6 // indirect github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/labstack/gommon v0.4.2 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect + github.com/labstack/gommon v0.5.0 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect + github.com/mattn/go-isatty v0.0.23 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oapi-codegen/oapi-codegen/v2 v2.5.0 // indirect - github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect - github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect - github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/oapi-codegen/oapi-codegen/v2 v2.8.0 // indirect + github.com/oasdiff/yaml v0.1.1 // indirect + github.com/oasdiff/yaml3 v0.0.14 // indirect github.com/peterbourgon/diskv/v3 v3.0.1 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -109,13 +104,14 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rogpeppe/fastuuid v1.2.0 // indirect - github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/shabbyrobe/xmlwriter v0.0.0-20230525083848-85336ec334fa // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/speakeasy-api/jsonpath v0.6.0 // indirect - github.com/speakeasy-api/openapi-overlay v0.10.2 // indirect + github.com/speakeasy-api/jsonpath v0.6.3 // indirect + github.com/speakeasy-api/openapi v1.24.0 // indirect github.com/spf13/pflag v1.0.9 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect @@ -138,17 +134,17 @@ require ( go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/dig v1.18.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/time v0.8.0 // indirect - golang.org/x/tools v0.27.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.54.0 // indirect + golang.org/x/mod v0.38.0 // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.48.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/grpc v1.68.0 // indirect google.golang.org/protobuf v1.35.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index cec2f0669..b1c3722d9 100644 --- a/go.sum +++ b/go.sum @@ -58,6 +58,8 @@ github.com/dgraph-io/ristretto v1.0.0/go.mod h1:jTi2FiYEhQ1NsMmA7DeBykizjOuY88Nh github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= +github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo= github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= @@ -85,8 +87,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= -github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/getkin/kin-openapi v0.142.0 h1:izj0vBdFprMhitfzaX8sTqztsEQyvwhssBoB6n8NO7w= +github.com/getkin/kin-openapi v0.142.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -94,21 +96,17 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s= +github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= +github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug= +github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -180,8 +178,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6 github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= @@ -197,21 +193,18 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.1.10/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= -github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= -github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/echo/v4 v4.15.4 h1:DL45vVYa+BWE+XuW+zZNd9H0YEdZ80UAWJGcTVW4EVs= +github.com/labstack/echo/v4 v4.15.4/go.mod h1:CuMetKIRwsuO/qlAgMq+KTAalwGoB/h4tC+yPdrTj1g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= -github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/labstack/gommon v0.5.0 h1:6VSQ2NOzsnEJ5W6+84E0RbcaDDmgB6NIAzWCczTEe6c= +github.com/labstack/gommon v0.5.0/go.mod h1:Rzlg7HHy1maLfzBYGg9NZcVuz1sA68HHhLjhcEllYE0= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ= +github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -230,16 +223,18 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oapi-codegen/echo-middleware v1.0.2 h1:oNBqiE7jd/9bfGNk/bpbX2nqWrtPc+LL4Boya8Wl81U= -github.com/oapi-codegen/echo-middleware v1.0.2/go.mod h1:5J6MFcGqrpWLXpbKGZtRPZViLIHyyyUHlkqg6dT2R4E= -github.com/oapi-codegen/oapi-codegen/v2 v2.5.0 h1:iJvF8SdB/3/+eGOXEpsWkD8FQAHj6mqkb6Fnsoc8MFU= -github.com/oapi-codegen/oapi-codegen/v2 v2.5.0/go.mod h1:fwlMxUEMuQK5ih9aymrxKPQqNm2n8bdLk1ppjH+lr9w= -github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= -github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= -github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= -github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= -github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= -github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/oapi-codegen/echo-middleware v1.1.0 h1:J0aQ2MOCbwejG/73QkPwtlfzz7iOUh8g/IOATjin/fc= +github.com/oapi-codegen/echo-middleware v1.1.0/go.mod h1:ClZP161QkAxUz9HJ6TaLyN96Y/SyvFaROsc3mQNz49A= +github.com/oapi-codegen/nullable v1.1.0 h1:eAh8JVc5430VtYVnq00Hrbpag9PFRGWLjxR1/3KntMs= +github.com/oapi-codegen/nullable v1.1.0/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY= +github.com/oapi-codegen/oapi-codegen/v2 v2.8.0 h1:s4hxMxuqtR8jPzXkBTtFwY/SBuj3gEAYikmbBSdtLMM= +github.com/oapi-codegen/oapi-codegen/v2 v2.8.0/go.mod h1:yae2TI9IYB5vxQ35gFrpXh9L5H1eJv4MAUK1jumGMTo= +github.com/oapi-codegen/runtime v1.6.0 h1:7Xx+GlueD6nRuyKoCPzL434Jfi3BetbiJOrzCHp/VPU= +github.com/oapi-codegen/runtime v1.6.0/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU= +github.com/oasdiff/yaml v0.1.1 h1:6nHx+pn9gBRM6YpBlFZFQGCCd1nuvqOBtTD3KKTgGxY= +github.com/oasdiff/yaml v0.1.1/go.mod h1:EYJNoyktvWMJ0Hmhx+6qTaqMOsalUaRGT8Sj1hNcegU= +github.com/oasdiff/yaml3 v0.0.14 h1:aLJee3hxBK2H5wdXd9iPcIXb93Nty1Ge0pT171eHtkw= +github.com/oasdiff/yaml3 v0.0.14/go.mod h1:csto2xfDjYccdUn/yw/bPjj/cYTdp6HtFA0J4TWG+gg= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -257,8 +252,6 @@ github.com/onsi/gomega v1.36.0 h1:Pb12RlruUtj4XUuPUqeEWc6j5DkVVVA49Uf6YLfC95Y= github.com/onsi/gomega v1.36.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/open-policy-agent/opa v0.70.0 h1:B3cqCN2iQAyKxK6+GI+N40uqkin+wzIrM7YA60t9x1U= github.com/open-policy-agent/opa v0.70.0/go.mod h1:Y/nm5NY0BX0BqjBriKUiV81sCl8XOjjvqQG7dXrggtI= -github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= -github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= @@ -285,9 +278,11 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqn github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= @@ -295,10 +290,10 @@ github.com/shabbyrobe/xmlwriter v0.0.0-20230525083848-85336ec334fa h1:qSlczoKLzv github.com/shabbyrobe/xmlwriter v0.0.0-20230525083848-85336ec334fa/go.mod h1:tKYSeHyJGYz7eoZMlzrRDQSfdYPYt0UduMr8b97Mmaw= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/speakeasy-api/jsonpath v0.6.0 h1:IhtFOV9EbXplhyRqsVhHoBmmYjblIRh5D1/g8DHMXJ8= -github.com/speakeasy-api/jsonpath v0.6.0/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw= -github.com/speakeasy-api/openapi-overlay v0.10.2 h1:VOdQ03eGKeiHnpb1boZCGm7x8Haj6gST0P3SGTX95GU= -github.com/speakeasy-api/openapi-overlay v0.10.2/go.mod h1:n0iOU7AqKpNFfEt6tq7qYITC4f0yzVVdFw0S7hukemg= +github.com/speakeasy-api/jsonpath v0.6.3 h1:c+QPwzAOdrWvzycuc9HFsIZcxKIaWcNpC+xhOW9rJxU= +github.com/speakeasy-api/jsonpath v0.6.3/go.mod h1:2cXloNuQ+RSXi5HTRaeBh7JEmjRXTiaKpFTdZiL7URI= +github.com/speakeasy-api/openapi v1.24.0 h1:opoD27rupX7zBVPq1HkIGLeMOzNNA7JalhYP8q34i04= +github.com/speakeasy-api/openapi v1.24.0/go.mod h1:g3+dIMe0AYgbbGvnlQZqesmjAVWSm9BmsjLevnefQrg= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= @@ -314,8 +309,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tealeg/xlsx/v3 v3.3.11 h1:LWh/bMLbiPQQPDEhqA7pMpRS1nP4TfO70FIsieWNADg= @@ -324,8 +319,6 @@ github.com/tidepool-org/go-common v0.12.2-0.20250129210214-bd36b59b9733 h1:WCOSr github.com/tidepool-org/go-common v0.12.2-0.20250129210214-bd36b59b9733/go.mod h1:BeqsQcDwfSsmnmc+/N/EOT8h3m8/YtqrLNykk5kGkv4= github.com/tidepool-org/platform v1.33.1-0.20240412022409-2d52339c4886 h1:QRQAUTRmKUtv7jmP+4dP746ApmC7P0M1ypVRYH8O+QY= github.com/tidepool-org/platform v1.33.1-0.20240412022409-2d52339c4886/go.mod h1:QMsN1a7sB5ujIhjYAI1abn26AajH6dCokCJ0OIq1DWI= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= @@ -388,18 +381,20 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -412,16 +407,16 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -441,11 +436,9 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -456,16 +449,16 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -498,7 +491,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/redox_models/gen_types.go b/redox_models/gen_types.go index e8899324c..10d92a2ef 100644 --- a/redox_models/gen_types.go +++ b/redox_models/gen_types.go @@ -1,10 +1,12 @@ // Package redox_models provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.8.0 DO NOT EDIT. package redox_models import ( "encoding/json" + + "github.com/oapi-codegen/runtime" ) // NewFlowsheet defines model for newFlowsheet. @@ -12,102 +14,102 @@ type NewFlowsheet struct { Meta struct { DataModel string `json:"DataModel"` Destinations *[]struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Destinations,omitempty"` - EventDateTime *string `json:"EventDateTime"` + EventDateTime *string `json:"EventDateTime,omitempty"` EventType string `json:"EventType"` - FacilityCode *string `json:"FacilityCode"` + FacilityCode *string `json:"FacilityCode,omitempty"` Logs *[]struct { - AttemptID *string `json:"AttemptID"` - ID *string `json:"ID"` + AttemptID *string `json:"AttemptID,omitempty"` + ID *string `json:"ID,omitempty"` } `json:"Logs,omitempty"` Message *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Message,omitempty"` Source *struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Source,omitempty"` - Test *bool `json:"Test"` + Test *bool `json:"Test,omitempty"` Transmission *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Transmission,omitempty"` } `json:"Meta"` Observations []struct { - AbnormalFlag *string `json:"AbnormalFlag"` + AbnormalFlag *string `json:"AbnormalFlag,omitempty"` Code string `json:"Code"` - Codeset *string `json:"Codeset"` + Codeset *string `json:"Codeset,omitempty"` DateTime string `json:"DateTime"` - Description *string `json:"Description"` + Description *string `json:"Description,omitempty"` Notes *[]interface{} `json:"Notes,omitempty"` Observer *struct { - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` } `json:"Observer,omitempty"` ReferenceRange *struct { - High *float32 `json:"High"` - Low *float32 `json:"Low"` - Text *string `json:"Text"` + High *float32 `json:"High,omitempty"` + Low *float32 `json:"Low,omitempty"` + Text *string `json:"Text,omitempty"` } `json:"ReferenceRange,omitempty"` - Status *string `json:"Status"` - Units *string `json:"Units"` + Status *string `json:"Status,omitempty"` + Units *string `json:"Units,omitempty"` Value string `json:"Value"` ValueType string `json:"ValueType"` } `json:"Observations"` Patient struct { Contacts *[]struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - LastName *string `json:"LastName"` - MiddleName *string `json:"MiddleName"` + FirstName *string `json:"FirstName,omitempty"` + LastName *string `json:"LastName,omitempty"` + MiddleName *string `json:"MiddleName,omitempty"` PhoneNumber *struct { - Home *string `json:"Home"` - Mobile *string `json:"Mobile"` - Office *string `json:"Office"` + Home *string `json:"Home,omitempty"` + Mobile *string `json:"Mobile,omitempty"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` - RelationToPatient *string `json:"RelationToPatient"` + RelationToPatient *string `json:"RelationToPatient,omitempty"` Roles *[]interface{} `json:"Roles,omitempty"` } `json:"Contacts,omitempty"` Demographics *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Citizenship *[]interface{} `json:"Citizenship,omitempty"` - DOB *string `json:"DOB"` - DeathDateTime *string `json:"DeathDateTime"` + DOB *string `json:"DOB,omitempty"` + DeathDateTime *string `json:"DeathDateTime,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - IsDeceased *bool `json:"IsDeceased"` - IsHispanic *bool `json:"IsHispanic"` - Language *string `json:"Language"` - LastName *string `json:"LastName"` - MaritalStatus *string `json:"MaritalStatus"` - MiddleName *string `json:"MiddleName"` + FirstName *string `json:"FirstName,omitempty"` + IsDeceased *bool `json:"IsDeceased,omitempty"` + IsHispanic *bool `json:"IsHispanic,omitempty"` + Language *string `json:"Language,omitempty"` + LastName *string `json:"LastName,omitempty"` + MaritalStatus *string `json:"MaritalStatus,omitempty"` + MiddleName *string `json:"MiddleName,omitempty"` PhoneNumber *struct { - Home *string `json:"Home"` - Mobile *string `json:"Mobile"` - Office *string `json:"Office"` + Home *string `json:"Home,omitempty"` + Mobile *string `json:"Mobile,omitempty"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` - Race *string `json:"Race"` - Religion *string `json:"Religion"` - SSN *string `json:"SSN"` - Sex *string `json:"Sex"` + Race *string `json:"Race,omitempty"` + Religion *string `json:"Religion,omitempty"` + SSN *string `json:"SSN,omitempty"` + Sex *string `json:"Sex,omitempty"` } `json:"Demographics,omitempty"` Identifiers []struct { ID string `json:"ID"` @@ -116,25 +118,25 @@ type NewFlowsheet struct { Notes *[]interface{} `json:"Notes,omitempty"` } `json:"Patient"` Visit *struct { - AccountNumber *string `json:"AccountNumber"` + AccountNumber *string `json:"AccountNumber,omitempty"` Extensions *map[string]interface{} `json:"Extensions,omitempty"` Location *struct { - Bed *string `json:"Bed"` - Department *string `json:"Department"` + Bed *string `json:"Bed,omitempty"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` - VisitDateTime *string `json:"VisitDateTime"` - VisitNumber *string `json:"VisitNumber"` + VisitDateTime *string `json:"VisitDateTime,omitempty"` + VisitNumber *string `json:"VisitNumber,omitempty"` } `json:"Visit,omitempty"` } @@ -143,180 +145,180 @@ type NewNotes struct { Meta struct { DataModel string `json:"DataModel"` Destinations *[]struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Destinations,omitempty"` - EventDateTime *string `json:"EventDateTime"` + EventDateTime *string `json:"EventDateTime,omitempty"` EventType string `json:"EventType"` - FacilityCode *string `json:"FacilityCode"` + FacilityCode *string `json:"FacilityCode,omitempty"` Logs *[]struct { - AttemptID *string `json:"AttemptID"` - ID *string `json:"ID"` + AttemptID *string `json:"AttemptID,omitempty"` + ID *string `json:"ID,omitempty"` } `json:"Logs,omitempty"` Message *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Message,omitempty"` Source *struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Source,omitempty"` - Test *bool `json:"Test"` + Test *bool `json:"Test,omitempty"` Transmission *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Transmission,omitempty"` } `json:"Meta"` Note struct { Authenticator *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"Authenticator,omitempty"` - Availability *string `json:"Availability"` + Availability *string `json:"Availability,omitempty"` Components *[]struct { - Comments *string `json:"Comments"` - ID *string `json:"ID"` - Name *string `json:"Name"` - Value *string `json:"Value"` + Comments *string `json:"Comments,omitempty"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` + Value *string `json:"Value,omitempty"` } `json:"Components,omitempty"` ContentType string `json:"ContentType"` - DocumentDescription *string `json:"DocumentDescription"` + DocumentDescription *string `json:"DocumentDescription,omitempty"` DocumentID string `json:"DocumentID"` DocumentType string `json:"DocumentType"` - DocumentationDateTime *string `json:"DocumentationDateTime"` - FileContents *string `json:"FileContents"` - FileName *string `json:"FileName"` - FileType *string `json:"FileType"` + DocumentationDateTime *string `json:"DocumentationDateTime,omitempty"` + FileContents *string `json:"FileContents,omitempty"` + FileName *string `json:"FileName,omitempty"` + FileType *string `json:"FileType,omitempty"` Notifications *[]struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"Notifications,omitempty"` Provider struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` + FirstName *string `json:"FirstName,omitempty"` ID string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"Provider"` - ServiceDateTime *string `json:"ServiceDateTime"` - Status *string `json:"Status"` + ServiceDateTime *string `json:"ServiceDateTime,omitempty"` + Status *string `json:"Status,omitempty"` } `json:"Note"` Orders *[]struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Orders,omitempty"` Patient struct { Demographics *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Citizenship *[]interface{} `json:"Citizenship,omitempty"` - DOB *string `json:"DOB"` - DeathDateTime *string `json:"DeathDateTime"` + DOB *string `json:"DOB,omitempty"` + DeathDateTime *string `json:"DeathDateTime,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - IsDeceased *bool `json:"IsDeceased"` - IsHispanic *bool `json:"IsHispanic"` - Language *string `json:"Language"` - LastName *string `json:"LastName"` - MaritalStatus *string `json:"MaritalStatus"` - MiddleName *string `json:"MiddleName"` + FirstName *string `json:"FirstName,omitempty"` + IsDeceased *bool `json:"IsDeceased,omitempty"` + IsHispanic *bool `json:"IsHispanic,omitempty"` + Language *string `json:"Language,omitempty"` + LastName *string `json:"LastName,omitempty"` + MaritalStatus *string `json:"MaritalStatus,omitempty"` + MiddleName *string `json:"MiddleName,omitempty"` PhoneNumber *struct { - Home *string `json:"Home"` - Mobile *string `json:"Mobile"` - Office *string `json:"Office"` + Home *string `json:"Home,omitempty"` + Mobile *string `json:"Mobile,omitempty"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` - Race *string `json:"Race"` - Religion *string `json:"Religion"` - SSN *string `json:"SSN"` - Sex *string `json:"Sex"` + Race *string `json:"Race,omitempty"` + Religion *string `json:"Religion,omitempty"` + SSN *string `json:"SSN,omitempty"` + Sex *string `json:"Sex,omitempty"` } `json:"Demographics,omitempty"` Identifiers []struct { ID string `json:"ID"` @@ -325,63 +327,63 @@ type NewNotes struct { Notes *[]interface{} `json:"Notes,omitempty"` } `json:"Patient"` Visit *struct { - AccountNumber *string `json:"AccountNumber"` + AccountNumber *string `json:"AccountNumber,omitempty"` AdditionalStaff *[]struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` Role *struct { - Code *string `json:"Code"` - Codeset *string `json:"Codeset"` - Description *string `json:"Description"` + Code *string `json:"Code,omitempty"` + Codeset *string `json:"Codeset,omitempty"` + Description *string `json:"Description,omitempty"` } `json:"Role,omitempty"` } `json:"AdditionalStaff,omitempty"` Location *struct { - Bed *string `json:"Bed"` - Department *string `json:"Department"` + Bed *string `json:"Bed,omitempty"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` - PatientClass *string `json:"PatientClass"` - VisitDateTime *string `json:"VisitDateTime"` - VisitNumber *string `json:"VisitNumber"` + PatientClass *string `json:"PatientClass,omitempty"` + VisitDateTime *string `json:"VisitDateTime,omitempty"` + VisitNumber *string `json:"VisitNumber,omitempty"` } `json:"Visit,omitempty"` } @@ -390,174 +392,174 @@ type NewOrder struct { Meta struct { DataModel string `json:"DataModel"` Destinations *[]struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Destinations,omitempty"` - EventDateTime *string `json:"EventDateTime"` + EventDateTime *string `json:"EventDateTime,omitempty"` EventType string `json:"EventType"` - FacilityCode *string `json:"FacilityCode"` + FacilityCode *string `json:"FacilityCode,omitempty"` Logs *[]struct { - AttemptID *string `json:"AttemptID"` - ID *string `json:"ID"` + AttemptID *string `json:"AttemptID,omitempty"` + ID *string `json:"ID,omitempty"` } `json:"Logs,omitempty"` Message *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Message,omitempty"` Source *struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Source,omitempty"` - Test *bool `json:"Test"` + Test *bool `json:"Test,omitempty"` Transmission *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Transmission,omitempty"` } `json:"Meta"` Order struct { - ApplicationOrderID *string `json:"ApplicationOrderID"` + ApplicationOrderID *string `json:"ApplicationOrderID,omitempty"` ClinicalInfo *[]struct { - Abbreviation *string `json:"Abbreviation"` - Code *string `json:"Code"` - Codeset *string `json:"Codeset"` - Description *string `json:"Description"` + Abbreviation *string `json:"Abbreviation,omitempty"` + Code *string `json:"Code,omitempty"` + Codeset *string `json:"Codeset,omitempty"` + Description *string `json:"Description,omitempty"` Notes *[]interface{} `json:"Notes,omitempty"` - Units *string `json:"Units"` - Value *string `json:"Value"` + Units *string `json:"Units,omitempty"` + Value *string `json:"Value,omitempty"` } `json:"ClinicalInfo,omitempty"` - CollectionDateTime *string `json:"CollectionDateTime"` - Comments *string `json:"Comments"` + CollectionDateTime *string `json:"CollectionDateTime,omitempty"` + Comments *string `json:"Comments,omitempty"` Diagnoses *[]struct { - Code *string `json:"Code"` - Codeset *string `json:"Codeset"` - DocumentedDateTime *string `json:"DocumentedDateTime"` - Name *string `json:"Name"` - Type *string `json:"Type"` + Code *string `json:"Code,omitempty"` + Codeset *string `json:"Codeset,omitempty"` + DocumentedDateTime *string `json:"DocumentedDateTime,omitempty"` + Name *string `json:"Name,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Diagnoses,omitempty"` - Expiration *string `json:"Expiration"` + Expiration *string `json:"Expiration,omitempty"` ID string `json:"ID"` Notes *[]interface{} `json:"Notes,omitempty"` OrderingFacility *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` - Name *string `json:"Name"` - PhoneNumber *string `json:"PhoneNumber"` + Name *string `json:"Name,omitempty"` + PhoneNumber *string `json:"PhoneNumber,omitempty"` } `json:"OrderingFacility,omitempty"` - Priority *string `json:"Priority"` + Priority *string `json:"Priority,omitempty"` Procedure *struct { - Code *string `json:"Code"` - Codeset *string `json:"Codeset"` - Description *string `json:"Description"` + Code *string `json:"Code,omitempty"` + Codeset *string `json:"Codeset,omitempty"` + Description *string `json:"Description,omitempty"` } `json:"Procedure,omitempty"` Provider *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` - NPI *string `json:"NPI"` + NPI *string `json:"NPI,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"Provider,omitempty"` ResultCopyProviders *[]struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"ResultCopyProviders,omitempty"` Specimen *struct { - BodySite *string `json:"BodySite"` - ID *string `json:"ID"` - Source *string `json:"Source"` + BodySite *string `json:"BodySite,omitempty"` + ID *string `json:"ID,omitempty"` + Source *string `json:"Source,omitempty"` } `json:"Specimen,omitempty"` - Status *string `json:"Status"` - TransactionDateTime *string `json:"TransactionDateTime"` + Status *string `json:"Status,omitempty"` + TransactionDateTime *string `json:"TransactionDateTime,omitempty"` } `json:"Order"` Patient struct { Demographics *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Citizenship *[]interface{} `json:"Citizenship,omitempty"` - DOB *string `json:"DOB"` - DeathDateTime *string `json:"DeathDateTime"` + DOB *string `json:"DOB,omitempty"` + DeathDateTime *string `json:"DeathDateTime,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - IsDeceased *bool `json:"IsDeceased"` - IsHispanic *bool `json:"IsHispanic"` - Language *string `json:"Language"` - LastName *string `json:"LastName"` - MaritalStatus *string `json:"MaritalStatus"` - MiddleName *string `json:"MiddleName"` + FirstName *string `json:"FirstName,omitempty"` + IsDeceased *bool `json:"IsDeceased,omitempty"` + IsHispanic *bool `json:"IsHispanic,omitempty"` + Language *string `json:"Language,omitempty"` + LastName *string `json:"LastName,omitempty"` + MaritalStatus *string `json:"MaritalStatus,omitempty"` + MiddleName *string `json:"MiddleName,omitempty"` PhoneNumber *struct { - Home *string `json:"Home"` - Mobile *string `json:"Mobile"` - Office *string `json:"Office"` + Home *string `json:"Home,omitempty"` + Mobile *string `json:"Mobile,omitempty"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` - Race *string `json:"Race"` - Religion *string `json:"Religion"` - SSN *string `json:"SSN"` - Sex *string `json:"Sex"` + Race *string `json:"Race,omitempty"` + Religion *string `json:"Religion,omitempty"` + SSN *string `json:"SSN,omitempty"` + Sex *string `json:"Sex,omitempty"` } `json:"Demographics,omitempty"` Identifiers []struct { ID string `json:"ID"` @@ -566,217 +568,217 @@ type NewOrder struct { Notes *[]interface{} `json:"Notes,omitempty"` } `json:"Patient"` Visit *struct { - AccountNumber *string `json:"AccountNumber"` + AccountNumber *string `json:"AccountNumber,omitempty"` AttendingProvider *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"AttendingProvider,omitempty"` ConsultingProvider *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"ConsultingProvider,omitempty"` Guarantor *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` - DOB *string `json:"DOB"` + DOB *string `json:"DOB,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` Employer *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` - Name *string `json:"Name"` - PhoneNumber *string `json:"PhoneNumber"` + Name *string `json:"Name,omitempty"` + PhoneNumber *string `json:"PhoneNumber,omitempty"` } `json:"Employer,omitempty"` - FirstName *string `json:"FirstName"` - LastName *string `json:"LastName"` - MiddleName *string `json:"MiddleName"` - Number *string `json:"Number"` + FirstName *string `json:"FirstName,omitempty"` + LastName *string `json:"LastName,omitempty"` + MiddleName *string `json:"MiddleName,omitempty"` + Number *string `json:"Number,omitempty"` PhoneNumber *struct { - Business *string `json:"Business"` - Home *string `json:"Home"` - Mobile *string `json:"Mobile"` + Business *string `json:"Business,omitempty"` + Home *string `json:"Home,omitempty"` + Mobile *string `json:"Mobile,omitempty"` } `json:"PhoneNumber,omitempty"` - RelationToPatient *string `json:"RelationToPatient"` - SSN *string `json:"SSN"` - Sex *string `json:"Sex"` + RelationToPatient *string `json:"RelationToPatient,omitempty"` + SSN *string `json:"SSN,omitempty"` + Sex *string `json:"Sex,omitempty"` Spouse *struct { - FirstName *string `json:"FirstName"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + LastName *string `json:"LastName,omitempty"` } `json:"Spouse,omitempty"` - Type *string `json:"Type"` + Type *string `json:"Type,omitempty"` } `json:"Guarantor,omitempty"` Insurances *[]struct { - AgreementType *string `json:"AgreementType"` + AgreementType *string `json:"AgreementType,omitempty"` Company *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - Name *string `json:"Name"` - PhoneNumber *string `json:"PhoneNumber"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + Name *string `json:"Name,omitempty"` + PhoneNumber *string `json:"PhoneNumber,omitempty"` } `json:"Company,omitempty"` - CoverageType *string `json:"CoverageType"` - EffectiveDate *string `json:"EffectiveDate"` - ExpirationDate *string `json:"ExpirationDate"` - GroupName *string `json:"GroupName"` - GroupNumber *string `json:"GroupNumber"` + CoverageType *string `json:"CoverageType,omitempty"` + EffectiveDate *string `json:"EffectiveDate,omitempty"` + ExpirationDate *string `json:"ExpirationDate,omitempty"` + GroupName *string `json:"GroupName,omitempty"` + GroupNumber *string `json:"GroupNumber,omitempty"` Insured *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` - DOB *string `json:"DOB"` - FirstName *string `json:"FirstName"` + DOB *string `json:"DOB,omitempty"` + FirstName *string `json:"FirstName,omitempty"` Identifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"Identifiers,omitempty"` - LastName *string `json:"LastName"` - MiddleName *string `json:"MiddleName"` - Relationship *string `json:"Relationship"` - SSN *string `json:"SSN"` - Sex *string `json:"Sex"` + LastName *string `json:"LastName,omitempty"` + MiddleName *string `json:"MiddleName,omitempty"` + Relationship *string `json:"Relationship,omitempty"` + SSN *string `json:"SSN,omitempty"` + Sex *string `json:"Sex,omitempty"` } `json:"Insured,omitempty"` - MemberNumber *string `json:"MemberNumber"` + MemberNumber *string `json:"MemberNumber,omitempty"` Plan *struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` - Name *string `json:"Name"` - Type *string `json:"Type"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + Name *string `json:"Name,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Plan,omitempty"` - PolicyNumber *string `json:"PolicyNumber"` - Priority *string `json:"Priority"` + PolicyNumber *string `json:"PolicyNumber,omitempty"` + Priority *string `json:"Priority,omitempty"` } `json:"Insurances,omitempty"` Location *struct { - Bed *string `json:"Bed"` - Department *string `json:"Department"` + Bed *string `json:"Bed,omitempty"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` - PatientClass *string `json:"PatientClass"` + PatientClass *string `json:"PatientClass,omitempty"` ReferringProvider *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"ReferringProvider,omitempty"` - VisitDateTime *string `json:"VisitDateTime"` - VisitNumber *string `json:"VisitNumber"` + VisitDateTime *string `json:"VisitDateTime,omitempty"` + VisitNumber *string `json:"VisitNumber,omitempty"` } `json:"Visit,omitempty"` } @@ -785,272 +787,272 @@ type NewResults struct { Meta struct { DataModel string `json:"DataModel"` Destinations *[]struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Destinations,omitempty"` - EventDateTime *string `json:"EventDateTime"` + EventDateTime *string `json:"EventDateTime,omitempty"` EventType string `json:"EventType"` - FacilityCode *string `json:"FacilityCode"` + FacilityCode *string `json:"FacilityCode,omitempty"` Logs *[]struct { - AttemptID *string `json:"AttemptID"` - ID *string `json:"ID"` + AttemptID *string `json:"AttemptID,omitempty"` + ID *string `json:"ID,omitempty"` } `json:"Logs,omitempty"` Message *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Message,omitempty"` Source *struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Source,omitempty"` - Test *bool `json:"Test"` + Test *bool `json:"Test,omitempty"` Transmission *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Transmission,omitempty"` } `json:"Meta"` Orders []struct { - ApplicationOrderID *string `json:"ApplicationOrderID"` - CollectionDateTime *string `json:"CollectionDateTime"` - CompletionDateTime *string `json:"CompletionDateTime"` + ApplicationOrderID *string `json:"ApplicationOrderID,omitempty"` + CollectionDateTime *string `json:"CollectionDateTime,omitempty"` + CompletionDateTime *string `json:"CompletionDateTime,omitempty"` ID string `json:"ID"` Notes *[]interface{} `json:"Notes,omitempty"` - Priority *string `json:"Priority"` + Priority *string `json:"Priority,omitempty"` Procedure *struct { - Code *string `json:"Code"` - Codeset *string `json:"Codeset"` - Description *string `json:"Description"` + Code *string `json:"Code,omitempty"` + Codeset *string `json:"Codeset,omitempty"` + Description *string `json:"Description,omitempty"` } `json:"Procedure,omitempty"` Provider *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` - NPI *string `json:"NPI"` + NPI *string `json:"NPI,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"Provider,omitempty"` - ResponseFlag *string `json:"ResponseFlag"` + ResponseFlag *string `json:"ResponseFlag,omitempty"` ResultCopyProviders *[]struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"ResultCopyProviders,omitempty"` Results []struct { - AbnormalFlag *string `json:"AbnormalFlag"` + AbnormalFlag *string `json:"AbnormalFlag,omitempty"` Code string `json:"Code"` - Codeset *string `json:"Codeset"` - CompletionDateTime *string `json:"CompletionDateTime"` - Description *string `json:"Description"` - FileType *string `json:"FileType"` + Codeset *string `json:"Codeset,omitempty"` + CompletionDateTime *string `json:"CompletionDateTime,omitempty"` + Description *string `json:"Description,omitempty"` + FileType *string `json:"FileType,omitempty"` Notes *[]interface{} `json:"Notes,omitempty"` ObservationMethod *struct { - Code *string `json:"Code"` - Codeset *string `json:"Codeset"` - Description *string `json:"Description"` + Code *string `json:"Code,omitempty"` + Codeset *string `json:"Codeset,omitempty"` + Description *string `json:"Description,omitempty"` } `json:"ObservationMethod,omitempty"` Performer *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"Performer,omitempty"` PrimaryResultsInterpreter *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` - NPI *string `json:"NPI"` + NPI *string `json:"NPI,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"PrimaryResultsInterpreter,omitempty"` Producer *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Producer,omitempty"` ReferenceRange *struct { - High *float32 `json:"High"` - Low *float32 `json:"Low"` - Text *string `json:"Text"` + High *float32 `json:"High,omitempty"` + Low *float32 `json:"Low,omitempty"` + Text *string `json:"Text,omitempty"` } `json:"ReferenceRange,omitempty"` - RelatedGroupID *string `json:"RelatedGroupID"` + RelatedGroupID *string `json:"RelatedGroupID,omitempty"` Specimen *struct { - BodySite *string `json:"BodySite"` - ID *string `json:"ID"` - Source *string `json:"Source"` + BodySite *string `json:"BodySite,omitempty"` + ID *string `json:"ID,omitempty"` + Source *string `json:"Source,omitempty"` } `json:"Specimen,omitempty"` - Status *string `json:"Status"` - Units *string `json:"Units"` + Status *string `json:"Status,omitempty"` + Units *string `json:"Units,omitempty"` Value string `json:"Value"` ValueType string `json:"ValueType"` } `json:"Results"` - ResultsStatus *string `json:"ResultsStatus"` + ResultsStatus *string `json:"ResultsStatus,omitempty"` Status string `json:"Status"` - TransactionDateTime *string `json:"TransactionDateTime"` + TransactionDateTime *string `json:"TransactionDateTime,omitempty"` } `json:"Orders"` Patient struct { Contacts *[]struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - LastName *string `json:"LastName"` - MiddleName *string `json:"MiddleName"` + FirstName *string `json:"FirstName,omitempty"` + LastName *string `json:"LastName,omitempty"` + MiddleName *string `json:"MiddleName,omitempty"` PhoneNumber *struct { - Home *string `json:"Home"` - Mobile *string `json:"Mobile"` - Office *string `json:"Office"` + Home *string `json:"Home,omitempty"` + Mobile *string `json:"Mobile,omitempty"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` - RelationToPatient *string `json:"RelationToPatient"` + RelationToPatient *string `json:"RelationToPatient,omitempty"` Roles *[]interface{} `json:"Roles,omitempty"` } `json:"Contacts,omitempty"` Demographics *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Citizenship *[]interface{} `json:"Citizenship,omitempty"` - DOB *string `json:"DOB"` - DeathDateTime *string `json:"DeathDateTime"` + DOB *string `json:"DOB,omitempty"` + DeathDateTime *string `json:"DeathDateTime,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - IsDeceased *bool `json:"IsDeceased"` - IsHispanic *bool `json:"IsHispanic"` - Language *string `json:"Language"` - LastName *string `json:"LastName"` - MaritalStatus *string `json:"MaritalStatus"` - MiddleName *string `json:"MiddleName"` + FirstName *string `json:"FirstName,omitempty"` + IsDeceased *bool `json:"IsDeceased,omitempty"` + IsHispanic *bool `json:"IsHispanic,omitempty"` + Language *string `json:"Language,omitempty"` + LastName *string `json:"LastName,omitempty"` + MaritalStatus *string `json:"MaritalStatus,omitempty"` + MiddleName *string `json:"MiddleName,omitempty"` PhoneNumber *struct { - Home *string `json:"Home"` - Mobile *string `json:"Mobile"` - Office *string `json:"Office"` + Home *string `json:"Home,omitempty"` + Mobile *string `json:"Mobile,omitempty"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` - Race *string `json:"Race"` - Religion *string `json:"Religion"` - SSN *string `json:"SSN"` - Sex *string `json:"Sex"` + Race *string `json:"Race,omitempty"` + Religion *string `json:"Religion,omitempty"` + SSN *string `json:"SSN,omitempty"` + Sex *string `json:"Sex,omitempty"` } `json:"Demographics,omitempty"` Identifiers []struct { ID string `json:"ID"` @@ -1059,129 +1061,129 @@ type NewResults struct { Notes *[]interface{} `json:"Notes,omitempty"` } `json:"Patient"` Visit *struct { - AccountNumber *string `json:"AccountNumber"` + AccountNumber *string `json:"AccountNumber,omitempty"` AdditionalStaff *[]struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` Role *struct { - Code *string `json:"Code"` - Codeset *string `json:"Codeset"` - Description *string `json:"Description"` + Code *string `json:"Code,omitempty"` + Codeset *string `json:"Codeset,omitempty"` + Description *string `json:"Description,omitempty"` } `json:"Role,omitempty"` } `json:"AdditionalStaff,omitempty"` AttendingProvider *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"AttendingProvider,omitempty"` Location *struct { - Bed *string `json:"Bed"` - Department *string `json:"Department"` + Bed *string `json:"Bed,omitempty"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` - PatientClass *string `json:"PatientClass"` + PatientClass *string `json:"PatientClass,omitempty"` ReferringProvider *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"ReferringProvider,omitempty"` - VisitDateTime *string `json:"VisitDateTime"` - VisitNumber *string `json:"VisitNumber"` + VisitDateTime *string `json:"VisitDateTime,omitempty"` + VisitNumber *string `json:"VisitNumber,omitempty"` } `json:"Visit,omitempty"` } @@ -1190,181 +1192,181 @@ type ReplaceNotes struct { Meta struct { DataModel string `json:"DataModel"` Destinations *[]struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Destinations,omitempty"` - EventDateTime *string `json:"EventDateTime"` + EventDateTime *string `json:"EventDateTime,omitempty"` EventType string `json:"EventType"` - FacilityCode *string `json:"FacilityCode"` + FacilityCode *string `json:"FacilityCode,omitempty"` Logs *[]struct { - AttemptID *string `json:"AttemptID"` - ID *string `json:"ID"` + AttemptID *string `json:"AttemptID,omitempty"` + ID *string `json:"ID,omitempty"` } `json:"Logs,omitempty"` Message *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Message,omitempty"` Source *struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Source,omitempty"` - Test *bool `json:"Test"` + Test *bool `json:"Test,omitempty"` Transmission *struct { - ID *float32 `json:"ID"` + ID *float32 `json:"ID,omitempty"` } `json:"Transmission,omitempty"` } `json:"Meta"` Note struct { Authenticator *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"Authenticator,omitempty"` - Availability *string `json:"Availability"` + Availability *string `json:"Availability,omitempty"` Components *[]struct { - Comments *string `json:"Comments"` - ID *string `json:"ID"` - Name *string `json:"Name"` - Value *string `json:"Value"` + Comments *string `json:"Comments,omitempty"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` + Value *string `json:"Value,omitempty"` } `json:"Components,omitempty"` ContentType string `json:"ContentType"` - DocumentDescription *string `json:"DocumentDescription"` + DocumentDescription *string `json:"DocumentDescription,omitempty"` DocumentID string `json:"DocumentID"` DocumentType string `json:"DocumentType"` - DocumentationDateTime *string `json:"DocumentationDateTime"` - FileContents *string `json:"FileContents"` - FileName *string `json:"FileName"` - FileType *string `json:"FileType"` + DocumentationDateTime *string `json:"DocumentationDateTime,omitempty"` + FileContents *string `json:"FileContents,omitempty"` + FileName *string `json:"FileName,omitempty"` + FileType *string `json:"FileType,omitempty"` Notifications *[]struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"Notifications,omitempty"` OriginalDocumentID string `json:"OriginalDocumentID"` Provider struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` + FirstName *string `json:"FirstName,omitempty"` ID string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` } `json:"Provider"` - ServiceDateTime *string `json:"ServiceDateTime"` - Status *string `json:"Status"` + ServiceDateTime *string `json:"ServiceDateTime,omitempty"` + Status *string `json:"Status,omitempty"` } `json:"Note"` Orders *[]struct { - ID *string `json:"ID"` - Name *string `json:"Name"` + ID *string `json:"ID,omitempty"` + Name *string `json:"Name,omitempty"` } `json:"Orders,omitempty"` Patient struct { Demographics *struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Citizenship *[]interface{} `json:"Citizenship,omitempty"` - DOB *string `json:"DOB"` - DeathDateTime *string `json:"DeathDateTime"` + DOB *string `json:"DOB,omitempty"` + DeathDateTime *string `json:"DeathDateTime,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - IsDeceased *bool `json:"IsDeceased"` - IsHispanic *bool `json:"IsHispanic"` - Language *string `json:"Language"` - LastName *string `json:"LastName"` - MaritalStatus *string `json:"MaritalStatus"` - MiddleName *string `json:"MiddleName"` + FirstName *string `json:"FirstName,omitempty"` + IsDeceased *bool `json:"IsDeceased,omitempty"` + IsHispanic *bool `json:"IsHispanic,omitempty"` + Language *string `json:"Language,omitempty"` + LastName *string `json:"LastName,omitempty"` + MaritalStatus *string `json:"MaritalStatus,omitempty"` + MiddleName *string `json:"MiddleName,omitempty"` PhoneNumber *struct { - Home *string `json:"Home"` - Mobile *string `json:"Mobile"` - Office *string `json:"Office"` + Home *string `json:"Home,omitempty"` + Mobile *string `json:"Mobile,omitempty"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` - Race *string `json:"Race"` - Religion *string `json:"Religion"` - SSN *string `json:"SSN"` - Sex *string `json:"Sex"` + Race *string `json:"Race,omitempty"` + Religion *string `json:"Religion,omitempty"` + SSN *string `json:"SSN,omitempty"` + Sex *string `json:"Sex,omitempty"` } `json:"Demographics,omitempty"` Identifiers []struct { ID string `json:"ID"` @@ -1373,63 +1375,63 @@ type ReplaceNotes struct { Notes *[]interface{} `json:"Notes,omitempty"` } `json:"Patient"` Visit *struct { - AccountNumber *string `json:"AccountNumber"` + AccountNumber *string `json:"AccountNumber,omitempty"` AdditionalStaff *[]struct { Address *struct { - City *string `json:"City"` - Country *string `json:"Country"` - County *string `json:"County"` - State *string `json:"State"` - StreetAddress *string `json:"StreetAddress"` - ZIP *string `json:"ZIP"` + City *string `json:"City,omitempty"` + Country *string `json:"Country,omitempty"` + County *string `json:"County,omitempty"` + State *string `json:"State,omitempty"` + StreetAddress *string `json:"StreetAddress,omitempty"` + ZIP *string `json:"ZIP,omitempty"` } `json:"Address,omitempty"` Credentials *[]interface{} `json:"Credentials,omitempty"` EmailAddresses *[]interface{} `json:"EmailAddresses,omitempty"` - FirstName *string `json:"FirstName"` - ID *string `json:"ID"` - IDType *string `json:"IDType"` - LastName *string `json:"LastName"` + FirstName *string `json:"FirstName,omitempty"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` + LastName *string `json:"LastName,omitempty"` Location *struct { - Department *string `json:"Department"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` PhoneNumber *struct { - Office *string `json:"Office"` + Office *string `json:"Office,omitempty"` } `json:"PhoneNumber,omitempty"` Role *struct { - Code *string `json:"Code"` - Codeset *string `json:"Codeset"` - Description *string `json:"Description"` + Code *string `json:"Code,omitempty"` + Codeset *string `json:"Codeset,omitempty"` + Description *string `json:"Description,omitempty"` } `json:"Role,omitempty"` } `json:"AdditionalStaff,omitempty"` Location *struct { - Bed *string `json:"Bed"` - Department *string `json:"Department"` + Bed *string `json:"Bed,omitempty"` + Department *string `json:"Department,omitempty"` DepartmentIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"DepartmentIdentifiers,omitempty"` - Facility *string `json:"Facility"` + Facility *string `json:"Facility,omitempty"` FacilityIdentifiers *[]struct { - ID *string `json:"ID"` - IDType *string `json:"IDType"` + ID *string `json:"ID,omitempty"` + IDType *string `json:"IDType,omitempty"` } `json:"FacilityIdentifiers,omitempty"` - Room *string `json:"Room"` - Type *string `json:"Type"` + Room *string `json:"Room,omitempty"` + Type *string `json:"Type,omitempty"` } `json:"Location,omitempty"` - PatientClass *string `json:"PatientClass"` - VisitDateTime *string `json:"VisitDateTime"` - VisitNumber *string `json:"VisitNumber"` + PatientClass *string `json:"PatientClass,omitempty"` + VisitDateTime *string `json:"VisitDateTime,omitempty"` + VisitNumber *string `json:"VisitNumber,omitempty"` } `json:"Visit,omitempty"` } @@ -1440,3 +1442,143 @@ type SendDataJSONBody struct { // SendDataJSONRequestBody defines body for SendData for application/json ContentType. type SendDataJSONRequestBody SendDataJSONBody + +// AsNewOrder returns the union data inside the SendDataJSONBody as a NewOrder +func (t SendDataJSONBody) AsNewOrder() (NewOrder, error) { + var body NewOrder + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNewOrder overwrites any union data inside the SendDataJSONBody as the provided NewOrder +func (t *SendDataJSONBody) FromNewOrder(v NewOrder) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNewOrder performs a merge with any union data inside the SendDataJSONBody, using the provided NewOrder +func (t *SendDataJSONBody) MergeNewOrder(v NewOrder) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsNewFlowsheet returns the union data inside the SendDataJSONBody as a NewFlowsheet +func (t SendDataJSONBody) AsNewFlowsheet() (NewFlowsheet, error) { + var body NewFlowsheet + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNewFlowsheet overwrites any union data inside the SendDataJSONBody as the provided NewFlowsheet +func (t *SendDataJSONBody) FromNewFlowsheet(v NewFlowsheet) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNewFlowsheet performs a merge with any union data inside the SendDataJSONBody, using the provided NewFlowsheet +func (t *SendDataJSONBody) MergeNewFlowsheet(v NewFlowsheet) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsNewNotes returns the union data inside the SendDataJSONBody as a NewNotes +func (t SendDataJSONBody) AsNewNotes() (NewNotes, error) { + var body NewNotes + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNewNotes overwrites any union data inside the SendDataJSONBody as the provided NewNotes +func (t *SendDataJSONBody) FromNewNotes(v NewNotes) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNewNotes performs a merge with any union data inside the SendDataJSONBody, using the provided NewNotes +func (t *SendDataJSONBody) MergeNewNotes(v NewNotes) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsNewResults returns the union data inside the SendDataJSONBody as a NewResults +func (t SendDataJSONBody) AsNewResults() (NewResults, error) { + var body NewResults + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNewResults overwrites any union data inside the SendDataJSONBody as the provided NewResults +func (t *SendDataJSONBody) FromNewResults(v NewResults) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNewResults performs a merge with any union data inside the SendDataJSONBody, using the provided NewResults +func (t *SendDataJSONBody) MergeNewResults(v NewResults) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsReplaceNotes returns the union data inside the SendDataJSONBody as a ReplaceNotes +func (t SendDataJSONBody) AsReplaceNotes() (ReplaceNotes, error) { + var body ReplaceNotes + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromReplaceNotes overwrites any union data inside the SendDataJSONBody as the provided ReplaceNotes +func (t *SendDataJSONBody) FromReplaceNotes(v ReplaceNotes) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeReplaceNotes performs a merge with any union data inside the SendDataJSONBody, using the provided ReplaceNotes +func (t *SendDataJSONBody) MergeReplaceNotes(v ReplaceNotes) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t SendDataJSONBody) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *SendDataJSONBody) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} diff --git a/spec/clinic.v1.yaml b/spec/clinic.v1.yaml index a4938b997..c58c84a65 100644 --- a/spec/clinic.v1.yaml +++ b/spec/clinic.v1.yaml @@ -321,6 +321,19 @@ paths: x-go-type-skip-optional-pointer: true x-omitempty: true in: query + - name: sites + description: | + Comma-separated list of clinic site IDs. If provided, only patients assigned to at least one of the given sites are included in the report. An empty value is ignored, as if the parameter were omitted. + allowEmptyValue: true + schema: + type: array + x-omitempty: true + x-omitzero: true + items: + $ref: '#/components/schemas/objectId.v1' + in: query + style: form + explode: false /v1/clinics/{clinicId}/patients: parameters: - $ref: '#/components/parameters/clinicId' @@ -948,21 +961,21 @@ paths: - schema: type: array items: - type: string - format: ^[a-f0-9]{24}$ + $ref: '#/components/schemas/objectIdOrZero.v1' in: query name: tags - description: Comma-separated list of patient tag IDs + description: | + Comma-separated list of patient tag IDs. The single value `_` filters for patients without any tags assigned; combining `_` with tag IDs is rejected. style: form explode: false - name: sites schema: type: array items: - type: string - format: ^[a-f0-9]{24}$ + $ref: '#/components/schemas/objectIdOrZero.v1' in: query - description: Comma-separated list of clinic site IDs + description: | + Comma-separated list of clinic site IDs. The single value `_` filters for patients without any sites assigned; combining `_` with site IDs is rejected. style: form explode: false - name: omitNonStandardRanges @@ -2183,6 +2196,7 @@ paths: responses: '200': description: OK + description: Get programs tags: - Clinics - Internal @@ -2194,7 +2208,7 @@ paths: responses: '200': description: OK - description: '' + description: Get program URL tags: - Clinics - Internal @@ -2206,7 +2220,7 @@ paths: responses: '200': description: OK - description: '' + description: Notification webhook tags: - Clinics - Internal @@ -2359,6 +2373,7 @@ components: type: integer x-go-type-skip-optional-pointer: true x-omitzero: true + readOnly: true required: - id - name @@ -3615,6 +3630,15 @@ components: maxLength: 24 pattern: ^[a-f0-9]{24}$ readOnly: true + siteIds.v1: + type: array + title: Site ID List + uniqueItems: true + nullable: true + x-omitzero: true + x-omitempty: true + items: + $ref: '#/components/schemas/siteId.v1' tideFilters.v1: type: object title: tidefilters.v1 @@ -3692,6 +3716,8 @@ components: x-go-type: float64 tags: $ref: '#/components/schemas/patientTagIds.v1' + sites: + $ref: '#/components/schemas/siteIds.v1' filters: $ref: '#/components/schemas/tideFilters.v1' required: @@ -3893,6 +3919,27 @@ components: required: - config - results + objectIdOrZero.v1: + title: Object Id or Zero Sentinel + type: string + description: A resource id, or the underscore sentinel `_` meaning "records with none assigned". + minLength: 1 + maxLength: 24 + pattern: ^([a-f0-9]{24}|_)$ + targetdevices.v1: + title: Target Devices + description: | + When uploading data, the user selects a target device (typically + make and model) of their data source (i.e. meter or pump) from a + list. This selection is used by the Tidepool Uploader to select the + correct drivers for communicating with the data source. + + These values are stored in the user's profile so that they can be + pre-selected on next use, thereby streamlining the process. + type: array + items: + type: string + example: bayercontournext patientPermissions.v1: type: object title: Patient Permissions @@ -4993,9 +5040,7 @@ components: tags: $ref: '#/components/schemas/patientTagIds.v1' targetDevices: - type: array - items: - type: string + $ref: '#/components/schemas/targetdevices.v1' permissions: $ref: '#/components/schemas/patientPermissions.v1' createdTime: diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/callback.go b/vendor/github.com/getkin/kin-openapi/openapi3/callback.go index 31f4e4be2..407f730a9 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/callback.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/callback.go @@ -2,14 +2,13 @@ package openapi3 import ( "context" - "sort" ) // Callback is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#callback-object type Callback struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` m map[string]*PathItem } @@ -39,23 +38,18 @@ func WithCallback(cb string, pathItem *PathItem) NewCallbackOption { func (callback *Callback) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) - keys := make([]string, 0, callback.Len()) - for key := range callback.Map() { - keys = append(keys, key) - } - sort.Strings(keys) - for _, key := range keys { + for _, key := range callback.Keys() { v := callback.Value(key) if err := v.Validate(ctx); err != nil { return err } } - return validateExtensions(ctx, callback.Extensions) + return validateExtensions(ctx, callback.Extensions, callback.Origin) } // UnmarshalJSON sets Callbacks to a copy of data. func (callbacks *Callbacks) UnmarshalJSON(data []byte) (err error) { - *callbacks, _, err = unmarshalStringMapP[CallbackRef](data) + *callbacks, err = unmarshalStringMapP[CallbackRef](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/components.go b/vendor/github.com/getkin/kin-openapi/openapi3/components.go index 890671aa7..b4b69fafb 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/components.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/components.go @@ -4,28 +4,26 @@ import ( "context" "encoding/json" "fmt" - "sort" + "maps" "github.com/go-openapi/jsonpointer" ) -type ( - Callbacks map[string]*CallbackRef - Examples map[string]*ExampleRef - Headers map[string]*HeaderRef - Links map[string]*LinkRef - ParametersMap map[string]*ParameterRef - RequestBodies map[string]*RequestBodyRef - ResponseBodies map[string]*ResponseRef - Schemas map[string]*SchemaRef - SecuritySchemes map[string]*SecuritySchemeRef -) +type Callbacks map[string]*CallbackRef // Callbacks represents components' named callbacks +type Examples map[string]*ExampleRef // Examples represents components' named examples +type Headers map[string]*HeaderRef // Headers represents components' named headers +type Links map[string]*LinkRef // Links represents components' named links +type ParametersMap map[string]*ParameterRef // ParametersMap represents components' named parameters +type RequestBodies map[string]*RequestBodyRef // RequestBodies represents components' named request bodies +type ResponseBodies map[string]*ResponseRef // ResponseBodies represents components' named response bodies +type Schemas map[string]*SchemaRef // Schemas represents components' named schemas +type SecuritySchemes map[string]*SecuritySchemeRef // SecuritySchemes represents components' named security schemes // Components is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#components-object type Components struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Schemas Schemas `json:"schemas,omitempty" yaml:"schemas,omitempty"` Parameters ParametersMap `json:"parameters,omitempty" yaml:"parameters,omitempty"` @@ -54,9 +52,7 @@ func (components Components) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Components. func (components Components) MarshalYAML() (any, error) { m := make(map[string]any, 9+len(components.Extensions)) - for k, v := range components.Extensions { - m[k] = v - } + maps.Copy(m, components.Extensions) if x := components.Schemas; len(x) != 0 { m["schemas"] = x } @@ -95,7 +91,6 @@ func (components *Components) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "schemas") delete(x.Extensions, "parameters") delete(x.Extensions, "headers") @@ -113,145 +108,86 @@ func (components *Components) UnmarshalJSON(data []byte) error { } // Validate returns an error if Components does not comply with the OpenAPI spec. -func (components *Components) Validate(ctx context.Context, opts ...ValidationOption) (err error) { +func (components *Components) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) - - schemas := make([]string, 0, len(components.Schemas)) - for name := range components.Schemas { - schemas = append(schemas, name) - } - sort.Strings(schemas) - for _, k := range schemas { - v := components.Schemas[k] - if err = ValidateIdentifier(k); err != nil { - return fmt.Errorf("schema %q: %w", k, err) - } - if err = v.Validate(ctx); err != nil { - return fmt.Errorf("schema %q: %w", k, err) + me := newErrCollector(ctx) + + validateMap := func(label string, names []string, validate func(k string) error) error { + for _, k := range names { + if idErr := ValidateIdentifier(k); idErr != nil { + if err := me.emit(&ComponentValidationError{Section: label, Name: k, Cause: idErr}); err != nil { + return err + } + // Skip validating the component's value when its name is + // invalid: any leaf error from validate(k) would surface as + // ": " and has no resolution path + // until the name is fixed. The continue keeps the noise + // per component bounded to a single, actionable finding. + continue + } + wrap := func(e error) error { return &ComponentValidationError{Section: label, Name: k, Cause: e} } + if err := me.emitWrapped(wrap, validate(k)); err != nil { + return err + } } + return nil } - parameters := make([]string, 0, len(components.Parameters)) - for name := range components.Parameters { - parameters = append(parameters, name) - } - sort.Strings(parameters) - for _, k := range parameters { - v := components.Parameters[k] - if err = ValidateIdentifier(k); err != nil { - return fmt.Errorf("parameter %q: %w", k, err) - } - if err = v.Validate(ctx); err != nil { - return fmt.Errorf("parameter %q: %w", k, err) - } + if err := validateMap("schema", componentNames(components.Schemas), func(k string) error { + return components.Schemas[k].Validate(ctx) + }); err != nil { + return err } - requestBodies := make([]string, 0, len(components.RequestBodies)) - for name := range components.RequestBodies { - requestBodies = append(requestBodies, name) - } - sort.Strings(requestBodies) - for _, k := range requestBodies { - v := components.RequestBodies[k] - if err = ValidateIdentifier(k); err != nil { - return fmt.Errorf("request body %q: %w", k, err) - } - if err = v.Validate(ctx); err != nil { - return fmt.Errorf("request body %q: %w", k, err) - } + if err := validateMap("parameter", componentNames(components.Parameters), func(k string) error { + return components.Parameters[k].Validate(ctx) + }); err != nil { + return err } - responses := make([]string, 0, len(components.Responses)) - for name := range components.Responses { - responses = append(responses, name) - } - sort.Strings(responses) - for _, k := range responses { - if err = ValidateIdentifier(k); err != nil { - return fmt.Errorf("response %q: %w", k, err) - } - v := components.Responses[k] - if err = v.Validate(ctx); err != nil { - return fmt.Errorf("response %q: %w", k, err) - } + if err := validateMap("request body", componentNames(components.RequestBodies), func(k string) error { + return components.RequestBodies[k].Validate(ctx) + }); err != nil { + return err } - headers := make([]string, 0, len(components.Headers)) - for name := range components.Headers { - headers = append(headers, name) - } - sort.Strings(headers) - for _, k := range headers { - v := components.Headers[k] - if err = ValidateIdentifier(k); err != nil { - return fmt.Errorf("header %q: %w", k, err) - } - if err = v.Validate(ctx); err != nil { - return fmt.Errorf("header %q: %w", k, err) - } + if err := validateMap("response", componentNames(components.Responses), func(k string) error { + return components.Responses[k].Validate(ctx) + }); err != nil { + return err } - securitySchemes := make([]string, 0, len(components.SecuritySchemes)) - for name := range components.SecuritySchemes { - securitySchemes = append(securitySchemes, name) - } - sort.Strings(securitySchemes) - for _, k := range securitySchemes { - v := components.SecuritySchemes[k] - if err = ValidateIdentifier(k); err != nil { - return fmt.Errorf("security scheme %q: %w", k, err) - } - if err = v.Validate(ctx); err != nil { - return fmt.Errorf("security scheme %q: %w", k, err) - } + if err := validateMap("header", componentNames(components.Headers), func(k string) error { + return components.Headers[k].Validate(ctx) + }); err != nil { + return err } - examples := make([]string, 0, len(components.Examples)) - for name := range components.Examples { - examples = append(examples, name) - } - sort.Strings(examples) - for _, k := range examples { - v := components.Examples[k] - if err = ValidateIdentifier(k); err != nil { - return fmt.Errorf("example %q: %w", k, err) - } - if err = v.Validate(ctx); err != nil { - return fmt.Errorf("example %q: %w", k, err) - } + if err := validateMap("security scheme", componentNames(components.SecuritySchemes), func(k string) error { + return components.SecuritySchemes[k].Validate(ctx) + }); err != nil { + return err } - links := make([]string, 0, len(components.Links)) - for name := range components.Links { - links = append(links, name) - } - sort.Strings(links) - for _, k := range links { - v := components.Links[k] - if err = ValidateIdentifier(k); err != nil { - return fmt.Errorf("link %q: %w", k, err) - } - if err = v.Validate(ctx); err != nil { - return fmt.Errorf("link %q: %w", k, err) - } + if err := validateMap("example", componentNames(components.Examples), func(k string) error { + return components.Examples[k].Validate(ctx) + }); err != nil { + return err } - callbacks := make([]string, 0, len(components.Callbacks)) - for name := range components.Callbacks { - callbacks = append(callbacks, name) + if err := validateMap("link", componentNames(components.Links), func(k string) error { + return components.Links[k].Validate(ctx) + }); err != nil { + return err } - sort.Strings(callbacks) - for _, k := range callbacks { - v := components.Callbacks[k] - if err = ValidateIdentifier(k); err != nil { - return fmt.Errorf("callback %q: %w", k, err) - } - if err = v.Validate(ctx); err != nil { - return fmt.Errorf("callback %q: %w", k, err) - } + + if err := validateMap("callback", componentNames(components.Callbacks), func(k string) error { + return components.Callbacks[k].Validate(ctx) + }); err != nil { + return err } - return validateExtensions(ctx, components.Extensions) + return me.finalize(validateExtensions(ctx, components.Extensions, components.Origin)) } var _ jsonpointer.JSONPointable = (*Schemas)(nil) diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/contact.go b/vendor/github.com/getkin/kin-openapi/openapi3/contact.go index 2ade3c68c..4a7bb3059 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/contact.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/contact.go @@ -3,13 +3,14 @@ package openapi3 import ( "context" "encoding/json" + "maps" ) // Contact is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#contact-object type Contact struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Name string `json:"name,omitempty" yaml:"name,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` @@ -28,9 +29,7 @@ func (contact Contact) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Contact. func (contact Contact) MarshalYAML() (any, error) { m := make(map[string]any, 3+len(contact.Extensions)) - for k, v := range contact.Extensions { - m[k] = v - } + maps.Copy(m, contact.Extensions) if x := contact.Name; x != "" { m["name"] = x } @@ -52,7 +51,6 @@ func (contact *Contact) UnmarshalJSON(data []byte) error { } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "url") delete(x.Extensions, "email") @@ -67,5 +65,5 @@ func (contact *Contact) UnmarshalJSON(data []byte) error { func (contact *Contact) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) - return validateExtensions(ctx, contact.Extensions) + return validateExtensions(ctx, contact.Extensions, contact.Origin) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/content.go b/vendor/github.com/getkin/kin-openapi/openapi3/content.go index 73e301e05..8bf7921a5 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/content.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/content.go @@ -2,7 +2,6 @@ package openapi3 import ( "context" - "sort" "strings" ) @@ -10,7 +9,7 @@ import ( type Content map[string]*MediaType func NewContent() Content { - return make(map[string]*MediaType) + return make(Content) } func NewContentWithSchema(schema *Schema, consumes []string) Content { @@ -109,14 +108,8 @@ func (content Content) Get(mime string) *MediaType { func (content Content) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) - keys := make([]string, 0, len(content)) - for key := range content { - keys = append(keys, key) - } - sort.Strings(keys) - for _, k := range keys { - v := content[k] - if err := v.Validate(ctx); err != nil { + for _, k := range componentNames(content) { + if err := content[k].Validate(ctx); err != nil { return err } } @@ -125,6 +118,6 @@ func (content Content) Validate(ctx context.Context, opts ...ValidationOption) e // UnmarshalJSON sets Content to a copy of data. func (content *Content) UnmarshalJSON(data []byte) (err error) { - *content, _, err = unmarshalStringMapP[MediaType](data) + *content, err = unmarshalStringMapP[MediaType](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go b/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go index 6934c170b..d797701c3 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go @@ -3,16 +3,31 @@ package openapi3 import ( "context" "encoding/json" + "maps" ) // Discriminator is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#discriminator-object type Discriminator struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` - PropertyName string `json:"propertyName" yaml:"propertyName"` // required - Mapping StringMap `json:"mapping,omitempty" yaml:"mapping,omitempty"` + PropertyName string `json:"propertyName" yaml:"propertyName"` // required + Mapping StringMap[MappingRef] `json:"mapping,omitempty" yaml:"mapping,omitempty"` +} + +// MappingRef is a ref to a Schema objects. Unlike SchemaRefs it is serialised +// as a plain string instead of an object with a $ref key, as such it also does +// not support extensions. +type MappingRef SchemaRef + +func (mr *MappingRef) UnmarshalText(data []byte) error { + mr.Ref = string(data) + return nil +} + +func (mr MappingRef) MarshalText() ([]byte, error) { + return []byte(mr.Ref), nil } // MarshalJSON returns the JSON encoding of Discriminator. @@ -27,9 +42,7 @@ func (discriminator Discriminator) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Discriminator. func (discriminator Discriminator) MarshalYAML() (any, error) { m := make(map[string]any, 2+len(discriminator.Extensions)) - for k, v := range discriminator.Extensions { - m[k] = v - } + maps.Copy(m, discriminator.Extensions) m["propertyName"] = discriminator.PropertyName if x := discriminator.Mapping; len(x) != 0 { m["mapping"] = x @@ -46,7 +59,6 @@ func (discriminator *Discriminator) UnmarshalJSON(data []byte) error { } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "propertyName") delete(x.Extensions, "mapping") if len(x.Extensions) == 0 { @@ -60,5 +72,5 @@ func (discriminator *Discriminator) UnmarshalJSON(data []byte) error { func (discriminator *Discriminator) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) - return validateExtensions(ctx, discriminator.Extensions) + return validateExtensions(ctx, discriminator.Extensions, discriminator.Origin) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/doc.go b/vendor/github.com/getkin/kin-openapi/openapi3/doc.go index 41c9965c6..83408e3de 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/doc.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/doc.go @@ -1,4 +1,32 @@ // Package openapi3 parses and writes OpenAPI 3 specification documents. // -// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md +// Supports OpenAPI 3.0, OpenAPI 3.1, and OpenAPI 3.2: +// - OpenAPI 3.0.x: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md +// - OpenAPI 3.1.x: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md +// - OpenAPI 3.2.x: https://spec.openapis.org/oas/v3.2.0.html +// +// OpenAPI 3.1 Features: +// - Type arrays with null support (e.g., ["string", "null"]) +// - JSON Schema 2020-12 keywords (const, examples, prefixItems, etc.) +// - Webhooks for defining callback operations +// - JSON Schema dialect specification +// - SPDX license identifiers +// +// OpenAPI 3.2 Features: +// - Media Type Object itemSchema for streaming sequential media types +// +// The implementation maintains 100% backward compatibility with OpenAPI 3.0. +// +// For OpenAPI 3.1 validation, use the JSON Schema 2020-12 validator option: +// +// schema.VisitJSON(value, openapi3.EnableJSONSchema2020()) +// +// Version detection is available via helper methods: +// +// if doc.IsOpenAPI31OrLater() { +// // Handle OpenAPI 3.1 specific features +// } +// if doc.IsOpenAPI32OrLater() { +// // Handle OpenAPI 3.2 specific features +// } package openapi3 diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go b/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go index 113eb730c..fd9316ae2 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go @@ -3,15 +3,14 @@ package openapi3 import ( "context" "encoding/json" - "fmt" - "sort" + "maps" ) // Encoding is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#encoding-object type Encoding struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` @@ -24,6 +23,15 @@ func NewEncoding() *Encoding { return &Encoding{} } +// Encodings is a map of encoding objects keyed by field name. +type Encodings map[string]*Encoding + +// UnmarshalJSON sets Encodings to a copy of data. +func (encodings *Encodings) UnmarshalJSON(data []byte) (err error) { + *encodings, err = unmarshalStringMapP[Encoding](data) + return +} + func (encoding *Encoding) WithHeader(name string, header *Header) *Encoding { return encoding.WithHeaderRef(name, &HeaderRef{ Value: header, @@ -52,9 +60,7 @@ func (encoding Encoding) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Encoding. func (encoding Encoding) MarshalYAML() (any, error) { m := make(map[string]any, 5+len(encoding.Extensions)) - for k, v := range encoding.Extensions { - m[k] = v - } + maps.Copy(m, encoding.Extensions) if x := encoding.ContentType; x != "" { m["contentType"] = x } @@ -82,7 +88,6 @@ func (encoding *Encoding) UnmarshalJSON(data []byte) error { } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "contentType") delete(x.Extensions, "headers") delete(x.Extensions, "style") @@ -118,12 +123,7 @@ func (encoding *Encoding) Validate(ctx context.Context, opts ...ValidationOption return nil } - headers := make([]string, 0, len(encoding.Headers)) - for k := range encoding.Headers { - headers = append(headers, k) - } - sort.Strings(headers) - for _, k := range headers { + for _, k := range componentNames(encoding.Headers) { v := encoding.Headers[k] if err := ValidateIdentifier(k); err != nil { return nil @@ -144,8 +144,8 @@ func (encoding *Encoding) Validate(ctx context.Context, opts ...ValidationOption sm.Style == SerializationPipeDelimited && !sm.Explode, sm.Style == SerializationDeepObject && sm.Explode: default: - return fmt.Errorf("serialization method with style=%q and explode=%v is not supported by media type", sm.Style, sm.Explode) + return newInvalidSerializationMethod("media type", sm.Style, sm.Explode, encoding.Origin) } - return validateExtensions(ctx, encoding.Extensions) + return validateExtensions(ctx, encoding.Extensions, encoding.Origin) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/error_collector.go b/vendor/github.com/getkin/kin-openapi/openapi3/error_collector.go new file mode 100644 index 000000000..b7331a7c3 --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/error_collector.go @@ -0,0 +1,82 @@ +package openapi3 + +import "context" + +// errCollector aggregates validation errors inside a Validate method. +// +// When multi-error mode is enabled (EnableMultiError), emit records the error +// and returns nil so the caller continues to the next sibling; if the error is +// itself a MultiError, its leaves are appended individually so the result is a +// flat MultiError of fully-wrapped problems (this matches what most consumers +// expect: one MultiError entry per independent problem). +// +// When multi-error mode is off, emit returns the error unchanged so the caller +// fails fast, preserving the historical behavior byte-for-byte. +// +// emitWrapped applies wrap to err, distributing wrap over each leaf when err +// is a MultiError. This is how validators attach per-section / per-path / +// per-operation context to each aggregated leaf. +// +// result returns the accumulated MultiError, or nil if none were recorded. +type errCollector struct { + multi bool + errs MultiError +} + +func newErrCollector(ctx context.Context) *errCollector { + return &errCollector{multi: getValidationOptions(ctx).multiErrorEnabled} +} + +func (c *errCollector) emit(err error) error { + if err == nil { + return nil + } + if !c.multi { + return err + } + if me, ok := err.(MultiError); ok { + for _, sub := range me { + if e := c.emit(sub); e != nil { + return e + } + } + return nil + } + c.errs = append(c.errs, err) + return nil +} + +func (c *errCollector) emitWrapped(wrap func(error) error, err error) error { + if err == nil { + return nil + } + if !c.multi { + return wrap(err) + } + if me, ok := err.(MultiError); ok { + for _, sub := range me { + if e := c.emitWrapped(wrap, sub); e != nil { + return e + } + } + return nil + } + return c.emit(wrap(err)) +} + +func (c *errCollector) result() error { + if len(c.errs) > 0 { + return c.errs + } + return nil +} + +// finalize emits err (typically the last sibling validation in a container, +// e.g. the extensions check) and returns the accumulated result. It collapses +// the trailing emit-then-result pattern into a single line at each call site. +func (c *errCollector) finalize(err error) error { + if e := c.emit(err); e != nil { + return e + } + return c.result() +} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/errors.go b/vendor/github.com/getkin/kin-openapi/openapi3/errors.go index 010dc889a..384af59ca 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/errors.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/errors.go @@ -57,3 +57,13 @@ func (meo multiErrorForOneOf) Error() string { func (meo multiErrorForOneOf) Unwrap() error { return MultiError(meo) } + +type multiErrorForAllOf MultiError + +func (mea multiErrorForAllOf) Error() string { + return spliceErr(" And ", mea) +} + +func (mea multiErrorForAllOf) Unwrap() error { + return MultiError(mea) +} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/example.go b/vendor/github.com/getkin/kin-openapi/openapi3/example.go index 44d5777e4..eeb4c6d4f 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/example.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/example.go @@ -3,14 +3,14 @@ package openapi3 import ( "context" "encoding/json" - "errors" + "maps" ) // Example is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#example-object type Example struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -34,9 +34,7 @@ func (example Example) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Example. func (example Example) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(example.Extensions)) - for k, v := range example.Extensions { - m[k] = v - } + maps.Copy(m, example.Extensions) if x := example.Summary; x != "" { m["summary"] = x } @@ -60,7 +58,6 @@ func (example *Example) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "summary") delete(x.Extensions, "description") delete(x.Extensions, "value") @@ -77,17 +74,17 @@ func (example *Example) Validate(ctx context.Context, opts ...ValidationOption) ctx = WithValidationOptions(ctx, opts...) if example.Value != nil && example.ExternalValue != "" { - return errors.New("value and externalValue are mutually exclusive") + return newExampleValueExternalValueExclusive(example.Origin) } if example.Value == nil && example.ExternalValue == "" { - return errors.New("no value or externalValue field") + return newExampleValueOrExternalValueRequired(example.Origin) } - return validateExtensions(ctx, example.Extensions) + return validateExtensions(ctx, example.Extensions, example.Origin) } // UnmarshalJSON sets Examples to a copy of data. func (examples *Examples) UnmarshalJSON(data []byte) (err error) { - *examples, _, err = unmarshalStringMapP[ExampleRef](data) + *examples, err = unmarshalStringMapP[ExampleRef](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/example_validation.go b/vendor/github.com/getkin/kin-openapi/openapi3/example_validation.go index 0d105c92d..1ecdcf3b8 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/example_validation.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/example_validation.go @@ -3,14 +3,18 @@ package openapi3 import "context" func validateExampleValue(ctx context.Context, input any, schema *Schema) error { - opts := make([]SchemaValidationOption, 0, 2) + opts := []SchemaValidationOption{MultiErrors()} - if vo := getValidationOptions(ctx); vo.examplesValidationAsReq { + vo := getValidationOptions(ctx) + if vo.examplesValidationAsReq { opts = append(opts, VisitAsRequest()) } else if vo.examplesValidationAsRes { opts = append(opts, VisitAsResponse()) } - opts = append(opts, MultiErrors()) + + if vo.jsonSchema2020ValidationEnabled { + opts = append(opts, EnableJSONSchema2020()) + } return schema.VisitJSON(input, opts...) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/extension.go b/vendor/github.com/getkin/kin-openapi/openapi3/extension.go index ca86078f2..e40502043 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/extension.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/extension.go @@ -2,16 +2,21 @@ package openapi3 import ( "context" - "fmt" - "sort" "strings" ) -func validateExtensions(ctx context.Context, extensions map[string]any) error { // FIXME: newtype + Validate(...) +// validateExtensions reports any non-`x-` keys in the given extensions +// map that are not explicitly allowed by the validation context. The +// origin argument is attached to the resulting ExtraSiblingFieldsError +// so callers can pin the finding to the parent object that carries the +// unknown keys; pass nil when the parent has no Origin (the loader was +// run with IncludeOrigin = false, or the parent was constructed +// programmatically without an Origin set). +func validateExtensions(ctx context.Context, extensions map[string]any, origin *Origin) error { // FIXME: newtype + Validate(...) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed var unknowns []string - for k := range extensions { + for _, k := range componentNames(extensions) { if strings.HasPrefix(k, "x-") { continue } @@ -24,8 +29,7 @@ func validateExtensions(ctx context.Context, extensions map[string]any) error { } if len(unknowns) != 0 { - sort.Strings(unknowns) - return fmt.Errorf("extra sibling fields: %+v", unknowns) + return newExtraSiblingFields(unknowns, origin) } return nil diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/external_docs.go b/vendor/github.com/getkin/kin-openapi/openapi3/external_docs.go index 7ff435ff6..b15ce9ac2 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/external_docs.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/external_docs.go @@ -3,8 +3,7 @@ package openapi3 import ( "context" "encoding/json" - "errors" - "fmt" + "maps" "net/url" ) @@ -12,7 +11,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#external-documentation-object type ExternalDocs struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Description string `json:"description,omitempty" yaml:"description,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` @@ -30,9 +29,7 @@ func (e ExternalDocs) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of ExternalDocs. func (e ExternalDocs) MarshalYAML() (any, error) { m := make(map[string]any, 2+len(e.Extensions)) - for k, v := range e.Extensions { - m[k] = v - } + maps.Copy(m, e.Extensions) if x := e.Description; x != "" { m["description"] = x } @@ -50,7 +47,6 @@ func (e *ExternalDocs) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "description") delete(x.Extensions, "url") if len(x.Extensions) == 0 { @@ -63,13 +59,18 @@ func (e *ExternalDocs) UnmarshalJSON(data []byte) error { // Validate returns an error if ExternalDocs does not comply with the OpenAPI spec. func (e *ExternalDocs) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) if e.URL == "" { - return errors.New("url is required") + if err := me.emit(newExternalDocsURLRequired(e.Origin)); err != nil { + return err + } } if _, err := url.Parse(e.URL); err != nil { - return fmt.Errorf("url is incorrect: %w", err) + if err := me.emit(&ExternalDocsURLValidationError{Cause: err}); err != nil { + return err + } } - return validateExtensions(ctx, e.Extensions) + return me.finalize(validateExtensions(ctx, e.Extensions, e.Origin)) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/header.go b/vendor/github.com/getkin/kin-openapi/openapi3/header.go index 6b23db52e..466c8ea21 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/header.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/header.go @@ -2,8 +2,6 @@ package openapi3 import ( "context" - "errors" - "fmt" "github.com/go-openapi/jsonpointer" ) @@ -54,10 +52,10 @@ func (header *Header) Validate(ctx context.Context, opts ...ValidationOption) er ctx = WithValidationOptions(ctx, opts...) if header.Name != "" { - return errors.New("header 'name' MUST NOT be specified, it is given in the corresponding headers map") + return newHeaderNameForbidden(header.Origin) } if header.In != "" { - return errors.New("header 'in' MUST NOT be specified, it is implicitly in header") + return newHeaderInForbidden(header.Origin) } // Validate a parameter's serialization method. @@ -68,28 +66,28 @@ func (header *Header) Validate(ctx context.Context, opts ...ValidationOption) er if smSupported := false || sm.Style == SerializationSimple && !sm.Explode || sm.Style == SerializationSimple && sm.Explode; !smSupported { - e := fmt.Errorf("serialization method with style=%q and explode=%v is not supported by a header parameter", sm.Style, sm.Explode) - return fmt.Errorf("header schema is invalid: %w", e) + e := newInvalidSerializationMethod("header", sm.Style, sm.Explode, header.Origin) + return &HeaderFieldValidationError{Field: "schema", Cause: e} } if (header.Schema == nil) == (len(header.Content) == 0) { - e := fmt.Errorf("parameter must contain exactly one of content and schema: %v", header) - return fmt.Errorf("header schema is invalid: %w", e) + return &HeaderFieldValidationError{Field: "schema", + Cause: newHeaderContentSchemaExactlyOne(header, header.Origin)} } if schema := header.Schema; schema != nil { if err := schema.Validate(ctx); err != nil { - return fmt.Errorf("header schema is invalid: %w", err) + return &HeaderFieldValidationError{Field: "schema", Cause: err} } } if content := header.Content; content != nil { - e := errors.New("parameter content must only contain one entry") if len(content) > 1 { - return fmt.Errorf("header content is invalid: %w", e) + return &HeaderFieldValidationError{Field: "content", + Cause: newHeaderContentSingleEntry(header.Origin)} } if err := content.Validate(ctx); err != nil { - return fmt.Errorf("header content is invalid: %w", err) + return &HeaderFieldValidationError{Field: "content", Cause: err} } } return nil @@ -97,6 +95,6 @@ func (header *Header) Validate(ctx context.Context, opts ...ValidationOption) er // UnmarshalJSON sets Headers to a copy of data. func (headers *Headers) UnmarshalJSON(data []byte) (err error) { - *headers, _, err = unmarshalStringMapP[HeaderRef](data) + *headers, err = unmarshalStringMapP[HeaderRef](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/helpers.go b/vendor/github.com/getkin/kin-openapi/openapi3/helpers.go index d50b3d847..62e8457cc 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/helpers.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/helpers.go @@ -6,7 +6,7 @@ import ( "path" "reflect" "regexp" - "sort" + "slices" "strings" "github.com/go-openapi/jsonpointer" @@ -31,22 +31,35 @@ func ValidateIdentifier(value string) error { return fmt.Errorf("identifier %q is not supported by OpenAPIv3 standard (charset: [%q])", value, identifierChars) } +// Ptr is a helper for defining OpenAPI schemas. +func Ptr[T any](value T) *T { + return &value +} + // Float64Ptr is a helper for defining OpenAPI schemas. +// +// Deprecated: Use Ptr instead. func Float64Ptr(value float64) *float64 { return &value } // BoolPtr is a helper for defining OpenAPI schemas. +// +// Deprecated: Use Ptr instead. func BoolPtr(value bool) *bool { return &value } // Int64Ptr is a helper for defining OpenAPI schemas. +// +// Deprecated: Use Ptr instead. func Int64Ptr(value int64) *int64 { return &value } // Uint64Ptr is a helper for defining OpenAPI schemas. +// +// Deprecated: Use Ptr instead. func Uint64Ptr(value uint64) *uint64 { return &value } @@ -57,7 +70,7 @@ func componentNames[E any](s map[string]E) []string { for i := range s { out = append(out, i) } - sort.Strings(out) + slices.Sort(out) return out } @@ -199,7 +212,7 @@ func ReferencesComponentInRootDocument(doc *T, ref ComponentRef) (string, bool) var components map[string]ComponentRef - componentRefType := reflect.TypeOf(new(ComponentRef)).Elem() + componentRefType := reflect.TypeFor[ComponentRef]() if t := reflect.TypeOf(collection); t.Kind() == reflect.Map && t.Key().Kind() == reflect.String && t.Elem().AssignableTo(componentRefType) { @@ -217,7 +230,8 @@ func ReferencesComponentInRootDocument(doc *T, ref ComponentRef) (string, bool) // Case 2: // Something like: ../openapi.yaml#/components/schemas/myElement - for name, s := range components { + for _, name := range componentNames(components) { + s := components[name] // Must be a reference to a YAML file. if !isWholeDocumentReference(s.RefString()) { continue diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/info.go b/vendor/github.com/getkin/kin-openapi/openapi3/info.go index ed5710e04..e4f08e638 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/info.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/info.go @@ -3,16 +3,18 @@ package openapi3 import ( "context" "encoding/json" - "errors" + "maps" ) // Info is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#info-object +// and https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#info-object type Info struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` - Title string `json:"title" yaml:"title"` // Required + Title string `json:"title" yaml:"title"` // Required + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` // OpenAPI >=3.1 Description string `json:"description,omitempty" yaml:"description,omitempty"` TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"` Contact *Contact `json:"contact,omitempty" yaml:"contact,omitempty"` @@ -34,11 +36,12 @@ func (info *Info) MarshalYAML() (any, error) { if info == nil { return nil, nil } - m := make(map[string]any, 6+len(info.Extensions)) - for k, v := range info.Extensions { - m[k] = v - } + m := make(map[string]any, 7+len(info.Extensions)) + maps.Copy(m, info.Extensions) m["title"] = info.Title + if x := info.Summary; x != "" { + m["summary"] = x + } if x := info.Description; x != "" { m["description"] = x } @@ -63,8 +66,8 @@ func (info *Info) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "title") + delete(x.Extensions, "summary") delete(x.Extensions, "description") delete(x.Extensions, "termsOfService") delete(x.Extensions, "contact") @@ -80,26 +83,37 @@ func (info *Info) UnmarshalJSON(data []byte) error { // Validate returns an error if Info does not comply with the OpenAPI spec. func (info *Info) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) + + if info.Summary != "" && !getValidationOptions(ctx).isOpenAPI31OrLater { + if err := me.emit(newInfoSummaryFieldFor31Plus(info.Origin)); err != nil { + return err + } + } if contact := info.Contact; contact != nil { - if err := contact.Validate(ctx); err != nil { + if err := me.emit(contact.Validate(ctx)); err != nil { return err } } if license := info.License; license != nil { - if err := license.Validate(ctx); err != nil { + if err := me.emit(license.Validate(ctx)); err != nil { return err } } if info.Version == "" { - return errors.New("value of version must be a non-empty string") + if err := me.emit(newInfoVersionRequired(info.Origin)); err != nil { + return err + } } if info.Title == "" { - return errors.New("value of title must be a non-empty string") + if err := me.emit(newInfoTitleRequired(info.Origin)); err != nil { + return err + } } - return validateExtensions(ctx, info.Extensions) + return me.finalize(validateExtensions(ctx, info.Extensions, info.Origin)) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/internalize_refs.go b/vendor/github.com/getkin/kin-openapi/openapi3/internalize_refs.go index 01f5dad88..fe8b379eb 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/internalize_refs.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/internalize_refs.go @@ -6,7 +6,7 @@ import ( "strings" ) -// RefNameResolver maps a component to an name that is used as it's internalized name. +// RefNameResolver maps a component to a name that is used as it's internalized name. // // The function should avoid name collisions (i.e. be a injective mapping). // It must only contain characters valid for fixed field names: [IdentifierRegExp]. @@ -23,7 +23,7 @@ type RefNameResolver func(*T, ComponentRef) string // - Cutting the "#/components/" part. // - Cutting the file extensions (.yaml/.json) from documents. // - Trimming the common directory with the root spec. -// - Replace invalid characters with with underscores. +// - Replace invalid characters with underscores. // // This is an injective mapping over a "reasonable" amount of the possible openapi // spec domain space but is not perfect. There might be edge cases. @@ -34,7 +34,7 @@ func DefaultRefNameResolver(doc *T, ref ComponentRef) string { name := ref.RefPath() - // If refering to a component in the root spec, no need to internalize just use + // If referring to a component in the root spec, no need to internalize just use // the existing component. // XXX(percivalalb): since this function call is iterating over components behind the // scenes during an internalization call it actually starts interating over @@ -63,25 +63,18 @@ func DefaultRefNameResolver(doc *T, ref ComponentRef) string { filePath = "" } - // Remove the path extentions to make this JSON/YAML agnostic. + // Remove the path extensions to make this JSON/YAML agnostic. for ext := path.Ext(filePath); len(ext) > 0; ext = path.Ext(filePath) { filePath = strings.TrimSuffix(filePath, ext) } // Trim the common prefix with the root doc path. if doc.url != nil { - commonDir := path.Dir(doc.url.Path) - for { - if commonDir == "." { // no common prefix - break - } - + for commonDir := path.Dir(doc.url.Path); /*no common prefix*/ commonDir != "."; commonDir = path.Dir(commonDir) { if p, found := cutDirectories(filePath, commonDir); found { filePath = p break } - - commonDir = path.Dir(commonDir) } } } @@ -119,7 +112,7 @@ func cutDirectories(p, dirs string) (string, bool) { var sb strings.Builder sb.Grow(len(ParameterInHeader)) - for _, segments := range strings.Split(p, "/") { + for segments := range strings.SplitSeq(p, "/") { sb.WriteString(segments) if sb.String() == p { @@ -352,6 +345,18 @@ func (doc *T) derefSchema(s *Schema, refNameResolver RefNameResolver, parentIsEx } } + // Discriminator mapping values are special cases since they are not full + // ref objects but are string references to schema objects. + if s.Discriminator != nil { + for _, k := range componentNames(s.Discriminator.Mapping) { + mapRef := s.Discriminator.Mapping[k] + s2 := (*SchemaRef)(&mapRef) + isExternal := doc.addSchemaToSpec(s2, refNameResolver, parentIsExternal) + doc.derefSchema(s2.Value, refNameResolver, isExternal || parentIsExternal) + s.Discriminator.Mapping[k] = MappingRef(*s2) + } + } + for _, name := range componentNames(s.Properties) { s2 := s.Properties[name] isExternal := doc.addSchemaToSpec(s2, refNameResolver, parentIsExternal) diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/license.go b/vendor/github.com/getkin/kin-openapi/openapi3/license.go index eb47fc385..358b1784f 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/license.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/license.go @@ -3,17 +3,22 @@ package openapi3 import ( "context" "encoding/json" - "errors" + "maps" ) // License is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#license-object +// and https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#license-object type License struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Name string `json:"name" yaml:"name"` // Required URL string `json:"url,omitempty" yaml:"url,omitempty"` + + // Identifier is an SPDX license expression for the API (OpenAPI 3.1) + // Either url or identifier can be specified, not both + Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` // OpenAPI >=3.1 } // MarshalJSON returns the JSON encoding of License. @@ -27,14 +32,15 @@ func (license License) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of License. func (license License) MarshalYAML() (any, error) { - m := make(map[string]any, 2+len(license.Extensions)) - for k, v := range license.Extensions { - m[k] = v - } + m := make(map[string]any, 3+len(license.Extensions)) + maps.Copy(m, license.Extensions) m["name"] = license.Name if x := license.URL; x != "" { m["url"] = x } + if x := license.Identifier; x != "" { + m["identifier"] = x + } return m, nil } @@ -46,9 +52,9 @@ func (license *License) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "url") + delete(x.Extensions, "identifier") if len(x.Extensions) == 0 { x.Extensions = nil } @@ -59,10 +65,25 @@ func (license *License) UnmarshalJSON(data []byte) error { // Validate returns an error if License does not comply with the OpenAPI spec. func (license *License) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) + + if license.Identifier != "" && !getValidationOptions(ctx).isOpenAPI31OrLater { + if err := me.emit(newLicenseIdentifierFieldFor31Plus(license.Origin)); err != nil { + return err + } + } if license.Name == "" { - return errors.New("value of license name must be a non-empty string") + if err := me.emit(newLicenseNameRequired(license.Origin)); err != nil { + return err + } + } + + if license.URL != "" && license.Identifier != "" { + if err := me.emit(newLicenseURLIdentifierExclusive(license.Origin)); err != nil { + return err + } } - return validateExtensions(ctx, license.Extensions) + return me.finalize(validateExtensions(ctx, license.Extensions, license.Origin)) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/link.go b/vendor/github.com/getkin/kin-openapi/openapi3/link.go index c76cf446d..60f78a029 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/link.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/link.go @@ -3,15 +3,14 @@ package openapi3 import ( "context" "encoding/json" - "errors" - "fmt" + "maps" ) // Link is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#link-object type Link struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"` OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` @@ -33,9 +32,7 @@ func (link Link) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Link. func (link Link) MarshalYAML() (any, error) { m := make(map[string]any, 6+len(link.Extensions)) - for k, v := range link.Extensions { - m[k] = v - } + maps.Copy(m, link.Extensions) if x := link.OperationRef; x != "" { m["operationRef"] = x @@ -68,7 +65,6 @@ func (link *Link) UnmarshalJSON(data []byte) error { } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "operationRef") delete(x.Extensions, "operationId") delete(x.Extensions, "description") @@ -87,17 +83,17 @@ func (link *Link) Validate(ctx context.Context, opts ...ValidationOption) error ctx = WithValidationOptions(ctx, opts...) if link.OperationID == "" && link.OperationRef == "" { - return errors.New("missing operationId or operationRef on link") + return newLinkOperationIDOrRefRequired(link.Origin) } if link.OperationID != "" && link.OperationRef != "" { - return fmt.Errorf("operationId %q and operationRef %q are mutually exclusive", link.OperationID, link.OperationRef) + return newLinkOperationIDRefExclusive(link.OperationID, link.OperationRef, link.Origin) } - return validateExtensions(ctx, link.Extensions) + return validateExtensions(ctx, link.Extensions, link.Origin) } // UnmarshalJSON sets Links to a copy of data. func (links *Links) UnmarshalJSON(data []byte) (err error) { - *links, _, err = unmarshalStringMapP[LinkRef](data) + *links, err = unmarshalStringMapP[LinkRef](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/loader.go b/vendor/github.com/getkin/kin-openapi/openapi3/loader.go index 436a1b3dc..f7fc8cd87 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/loader.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/loader.go @@ -15,27 +15,46 @@ import ( "strings" ) -// IncludeOrigin specifies whether to include the origin of the OpenAPI elements -// Set this to true before loading a spec to include the origin of the OpenAPI elements -// Note it is global and affects all loaders +// IncludeOrigin specifies whether to include the origin of the OpenAPI elements. +// Deprecated: set Loader.IncludeOrigin instead. This global is read by NewLoader +// for backward compatibility but is not safe for concurrent use. var IncludeOrigin = false -func foundUnresolvedRef(ref string) error { - return fmt.Errorf("found unresolved ref: %q", ref) -} - func failedToResolveRefFragmentPart(value, what string) error { return fmt.Errorf("failed to resolve %q in fragment in URI: %q", what, value) } // Loader helps deserialize an OpenAPIv3 document type Loader struct { - // IsExternalRefsAllowed enables visiting other files + // IsExternalRefsAllowed enables visiting other files. Enforced only when + // ReadFromURIFunc is nil; a custom ReadFromURIFunc bypasses this flag and + // owns the access policy itself — see ReadFromURIFunc. IsExternalRefsAllowed bool - // ReadFromURIFunc allows overriding the any file/URL reading func + // IncludeOrigin enables recording the file/line/column of each OpenAPI element. + // Prefer this over the package-level IncludeOrigin global, which is not safe for + // concurrent use. + IncludeOrigin bool + + // ReadFromURIFunc overrides how the loader reads a referenced file or URL. + // + // SECURITY: when a custom ReadFromURIFunc is set, IsExternalRefsAllowed is + // NOT enforced — this function alone decides which locations may be read. A + // func that reads whatever URI it is handed (e.g. by delegating to + // DefaultReadFromURI) resolves external $refs even when IsExternalRefsAllowed + // is false, which on untrusted documents enables local file reads + // (`$ref: "/etc/passwd"`) and SSRF (`$ref: "http://169.254.169.254/..."`). + // A custom func must apply its own scheme/host allowlist, or re-check + // IsExternalRefsAllowed, before reading. ReadFromURIFunc ReadFromURIFunc + // JoinFunc allows overriding how relative $ref paths are resolved against + // a base path. When set, it is called instead of the default join logic + // that uses path.Dir and path.Join. This is useful when loading specs from + // non-filesystem sources (e.g. git objects, remote archives) where the base + // path follows a different convention than filesystem paths. + JoinFunc func(basePath *url.URL, relativePath *url.URL) *url.URL + Context context.Context rootDir string @@ -53,7 +72,8 @@ type Loader struct { // NewLoader returns an empty Loader func NewLoader() *Loader { return &Loader{ - Context: context.Background(), + Context: context.Background(), + IncludeOrigin: IncludeOrigin, } } @@ -92,11 +112,16 @@ func (loader *Loader) allowsExternalRefs(ref string) (err error) { } func (loader *Loader) loadSingleElementFromURI(ref string, rootPath *url.URL, element any) (*url.URL, error) { - if err := loader.allowsExternalRefs(ref); err != nil { - return nil, err + // IsExternalRefsAllowed is enforced here only when no custom ReadFromURIFunc + // is installed; otherwise the custom func owns the access policy (see the + // SECURITY note on the ReadFromURIFunc field). + if loader.ReadFromURIFunc == nil { + if err := loader.allowsExternalRefs(ref); err != nil { + return nil, err + } } - resolvedPath, err := resolvePathWithRef(ref, rootPath) + resolvedPath, err := loader.resolvePathWithRef(ref, rootPath) if err != nil { return nil, err } @@ -108,7 +133,7 @@ func (loader *Loader) loadSingleElementFromURI(ref string, rootPath *url.URL, el if err != nil { return nil, err } - if err := unmarshal(data, element, IncludeOrigin); err != nil { + if err := unmarshal(data, element, loader.IncludeOrigin, resolvedPath); err != nil { return nil, err } @@ -144,7 +169,7 @@ func (loader *Loader) LoadFromIoReader(reader io.Reader) (*T, error) { func (loader *Loader) LoadFromData(data []byte) (*T, error) { loader.resetVisitedPathItemRefs() doc := &T{} - if err := unmarshal(data, doc, IncludeOrigin); err != nil { + if err := unmarshal(data, doc, loader.IncludeOrigin, nil); err != nil { return nil, err } if err := loader.ResolveRefsIn(doc, nil); err != nil { @@ -173,7 +198,7 @@ func (loader *Loader) loadFromDataWithPathInternal(data []byte, location *url.UR doc := &T{} loader.visitedDocuments[uri] = doc - if err := unmarshal(data, doc, IncludeOrigin); err != nil { + if err := unmarshal(data, doc, loader.IncludeOrigin, location); err != nil { return nil, err } @@ -259,10 +284,18 @@ func (loader *Loader) ResolveRefsIn(doc *T, location *url.URL) (err error) { } } + for _, name := range componentNames(doc.Webhooks) { + if pathItem := doc.Webhooks[name]; pathItem != nil { + if err = loader.resolvePathItemRef(doc, pathItem, location); err != nil { + return + } + } + } + return } -func join(basePath *url.URL, relativePath *url.URL) *url.URL { +func defaultJoin(basePath *url.URL, relativePath *url.URL) *url.URL { if basePath == nil { return relativePath } @@ -271,24 +304,27 @@ func join(basePath *url.URL, relativePath *url.URL) *url.URL { return &newPath } -func resolvePath(basePath *url.URL, componentPath *url.URL) *url.URL { +func (loader *Loader) resolvePath(basePath *url.URL, componentPath *url.URL) *url.URL { if is_file(componentPath) { // support absolute paths if filepath.IsAbs(componentPath.Path) { return componentPath } - return join(basePath, componentPath) + if loader.JoinFunc != nil { + return loader.JoinFunc(basePath, componentPath) + } + return defaultJoin(basePath, componentPath) } return componentPath } -func resolvePathWithRef(ref string, rootPath *url.URL) (*url.URL, error) { +func (loader *Loader) resolvePathWithRef(ref string, rootPath *url.URL) (*url.URL, error) { parsedURL, err := url.Parse(ref) if err != nil { return nil, fmt.Errorf("cannot parse reference: %q: %w", ref, err) } - resolvedPath := resolvePath(rootPath, parsedURL) + resolvedPath := loader.resolvePath(rootPath, parsedURL) resolvedPath.Fragment = parsedURL.Fragment return resolvedPath, nil } @@ -306,11 +342,16 @@ func (loader *Loader) resolveRefPath(ref string, path *url.URL) (*url.URL, error return path, nil } - if err := loader.allowsExternalRefs(ref); err != nil { - return nil, err + // IsExternalRefsAllowed is enforced here only when no custom ReadFromURIFunc + // is installed; otherwise the custom func owns the access policy (see the + // SECURITY note on the ReadFromURIFunc field). + if loader.ReadFromURIFunc == nil { + if err := loader.allowsExternalRefs(ref); err != nil { + return nil, err + } } - resolvedPath, err := resolvePathWithRef(ref, path) + resolvedPath, err := loader.resolvePathWithRef(ref, path) if err != nil { return nil, err } @@ -372,7 +413,7 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv } drill := func(cursor any) (any, error) { - for _, pathPart := range strings.Split(fragment[1:], "/") { + for pathPart := range strings.SplitSeq(fragment[1:], "/") { pathPart = unescapeRefString(pathPart) attempted := false @@ -427,7 +468,7 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv if err2 != nil { return nil, nil, err } - if err2 = unmarshal(data, &cursor, IncludeOrigin); err2 != nil { + if err2 = unmarshal(data, &cursor, loader.IncludeOrigin, path); err2 != nil { return nil, nil, err } if cursor, err2 = drill(cursor); err2 != nil || cursor == nil { @@ -459,7 +500,7 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv reflect.ValueOf(resolved).Elem().Set(reflect.ValueOf(cursor).Elem()) return componentDoc, componentPath, nil - case reflect.TypeOf(cursor) == reflect.TypeOf(map[string]any{}): + case reflect.TypeOf(cursor) == reflect.TypeFor[map[string]any](): codec := func(got, expect any) error { enc, err := json.Marshal(got) if err != nil { @@ -534,10 +575,10 @@ func drillIntoField(cursor any, fieldName string) (any, error) { case reflect.Struct: hasFields := false - for i := 0; i < val.NumField(); i++ { + for i := range val.NumField() { hasFields = true if yamlTag := val.Type().Field(i).Tag.Get("yaml"); yamlTag != "-" { - if tagName := strings.Split(yamlTag, ",")[0]; tagName != "" { + if tagName, _, _ := strings.Cut(yamlTag, ","); tagName != "" { if fieldName == tagName { return val.Field(i).Interface(), nil } @@ -657,6 +698,11 @@ func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPat return err } } + for _, k := range componentNames(value.Examples) { + if err := loader.resolveExampleRef(doc, value.Examples[k], documentPath); err != nil { + return err + } + } return nil } @@ -710,11 +756,8 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum return errors.New("cannot contain both schema and content in a parameter") } for _, name := range componentNames(value.Content) { - contentType := value.Content[name] - if schema := contentType.Schema; schema != nil { - if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { - return err - } + if err := loader.resolveMediaTypeRefs(doc, value.Content[name], documentPath); err != nil { + return err } } if schema := value.Schema; schema != nil { @@ -722,6 +765,11 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum return err } } + for _, k := range componentNames(value.Examples) { + if err := loader.resolveExampleRef(doc, value.Examples[k], documentPath); err != nil { + return err + } + } return nil } @@ -772,21 +820,8 @@ func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, d } for _, name := range componentNames(value.Content) { - contentType := value.Content[name] - if contentType == nil { - continue - } - for _, name := range componentNames(contentType.Examples) { - example := contentType.Examples[name] - if err := loader.resolveExampleRef(doc, example, documentPath); err != nil { - return err - } - contentType.Examples[name] = example - } - if schema := contentType.Schema; schema != nil { - if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { - return err - } + if err := loader.resolveMediaTypeRefs(doc, value.Content[name], documentPath); err != nil { + return err } } return nil @@ -845,22 +880,8 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen } } for _, name := range componentNames(value.Content) { - contentType := value.Content[name] - if contentType == nil { - continue - } - for _, name := range componentNames(contentType.Examples) { - example := contentType.Examples[name] - if err := loader.resolveExampleRef(doc, example, documentPath); err != nil { - return err - } - contentType.Examples[name] = example - } - if schema := contentType.Schema; schema != nil { - if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { - return err - } - contentType.Schema = schema + if err := loader.resolveMediaTypeRefs(doc, value.Content[name], documentPath); err != nil { + return err } } for _, name := range componentNames(value.Links) { @@ -872,6 +893,30 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen return nil } +func (loader *Loader) resolveMediaTypeRefs(doc *T, mediaType *MediaType, documentPath *url.URL) (err error) { + if mediaType == nil { + return + } + if schema := mediaType.Schema; schema != nil { + if err = loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { + return + } + } + if itemSchema := mediaType.ItemSchema; itemSchema != nil { + if err = loader.resolveSchemaRef(doc, itemSchema, documentPath, []string{}); err != nil { + return + } + } + for _, name := range componentNames(mediaType.Examples) { + example := mediaType.Examples[name] + if err = loader.resolveExampleRef(doc, example, documentPath); err != nil { + return + } + mediaType.Examples[name] = example + } + return +} + func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPath *url.URL, visited []string) (err error) { if component.isEmpty() { return errMUSTSchema @@ -912,6 +957,18 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) + + // OAS 3.1 / JSON Schema 2020-12: apply sibling keywords from the original schema + // object on top of the resolved $ref value. In 3.1, siblings are not ignored — + // they augment the referenced schema (e.g. deprecated:true alongside $ref). + // Only apply for OAS 3.1+ — in 3.0 $ref replaces its entire object and siblings + // are (validly) ignored. + if doc.IsOpenAPI31OrLater() && component.sibling != nil && component.Value != nil { + // Work on a copy so we don't mutate a schema shared by other references. + schemaCopy := *component.Value + applySiblingSchemaFields(&schemaCopy, component.sibling, component.extra) + component.Value = &schemaCopy + } } value := component.Value if value == nil { @@ -955,6 +1012,89 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat return err } } + // Discriminator mapping refs are a special case since they are not full + // ref objects but are plain strings that reference schema objects. + // Only resolve refs that look like external references (contain a path). + // Plain schema names like "Dog" or internal refs like "#/components/schemas/Dog" + // don't need to be resolved by the loader. + if value.Discriminator != nil { + for _, k := range componentNames(value.Discriminator.Mapping) { + v := value.Discriminator.Mapping[k] + // Only resolve if it looks like an external ref (contains path separator) + if strings.Contains(v.Ref, "/") && !strings.HasPrefix(v.Ref, "#") { + if err := loader.resolveSchemaRef(doc, (*SchemaRef)(&v), documentPath, visited); err != nil { + return err + } + value.Discriminator.Mapping[k] = v + } + } + } + + // OpenAPI 3.1 / JSON Schema 2020-12 fields + for _, v := range value.PrefixItems { + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + if v := value.Contains; v != nil { + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + for _, name := range componentNames(value.PatternProperties) { + v := value.PatternProperties[name] + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + for _, name := range componentNames(value.DependentSchemas) { + v := value.DependentSchemas[name] + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + for _, name := range componentNames(value.Defs) { + v := value.Defs[name] + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + if v := value.PropertyNames; v != nil { + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + if v := value.UnevaluatedItems.Schema; v != nil { + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + if v := value.UnevaluatedProperties.Schema; v != nil { + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + if v := value.If; v != nil { + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + if v := value.Then; v != nil { + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + if v := value.Else; v != nil { + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + if v := value.ContentSchema; v != nil { + if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { + return err + } + } + return nil } @@ -1214,5 +1354,139 @@ func (loader *Loader) resolvePathItemRef(doc *T, pathItem *PathItem, documentPat } func unescapeRefString(ref string) string { - return strings.Replace(strings.Replace(ref, "~1", "/", -1), "~0", "~", -1) + return strings.ReplaceAll(strings.ReplaceAll(ref, "~1", "/"), "~0", "~") +} + +// applySiblingSchemaFields overlays the fields listed in presentFields from sibling onto dst. +// It is used to honour keyword siblings of $ref in OpenAPI 3.1 / JSON Schema 2020-12, where +// sibling keywords are applied in addition to (not instead of) the referenced schema. +// Only fields that were explicitly present in the original YAML/JSON are applied; the presentFields +// slice (derived from SchemaRef.extra) carries this information. +func applySiblingSchemaFields(dst, sibling *Schema, presentFields []string) { + for _, field := range presentFields { + switch field { + case "oneOf": + dst.OneOf = sibling.OneOf + case "anyOf": + dst.AnyOf = sibling.AnyOf + case "allOf": + dst.AllOf = sibling.AllOf + case "not": + dst.Not = sibling.Not + case "type": + dst.Type = sibling.Type + case "title": + dst.Title = sibling.Title + case "format": + dst.Format = sibling.Format + case "description": + dst.Description = sibling.Description + case "enum": + dst.Enum = sibling.Enum + case "default": + dst.Default = sibling.Default + case "example": + dst.Example = sibling.Example + case "externalDocs": + dst.ExternalDocs = sibling.ExternalDocs + case "uniqueItems": + dst.UniqueItems = sibling.UniqueItems + case "exclusiveMinimum": + dst.ExclusiveMin = sibling.ExclusiveMin + case "exclusiveMaximum": + dst.ExclusiveMax = sibling.ExclusiveMax + case "nullable": + dst.Nullable = sibling.Nullable + case "readOnly": + dst.ReadOnly = sibling.ReadOnly + case "writeOnly": + dst.WriteOnly = sibling.WriteOnly + case "allowEmptyValue": + dst.AllowEmptyValue = sibling.AllowEmptyValue + case "deprecated": + dst.Deprecated = sibling.Deprecated + case "xml": + dst.XML = sibling.XML + case "minimum": + dst.Min = sibling.Min + case "maximum": + dst.Max = sibling.Max + case "multipleOf": + dst.MultipleOf = sibling.MultipleOf + case "minLength": + dst.MinLength = sibling.MinLength + case "maxLength": + dst.MaxLength = sibling.MaxLength + case "pattern": + dst.Pattern = sibling.Pattern + case "minItems": + dst.MinItems = sibling.MinItems + case "maxItems": + dst.MaxItems = sibling.MaxItems + case "items": + dst.Items = sibling.Items + case "required": + dst.Required = sibling.Required + case "properties": + dst.Properties = sibling.Properties + case "minProperties": + dst.MinProps = sibling.MinProps + case "maxProperties": + dst.MaxProps = sibling.MaxProps + case "additionalProperties": + dst.AdditionalProperties = sibling.AdditionalProperties + case "discriminator": + dst.Discriminator = sibling.Discriminator + case "const": + dst.Const = sibling.Const + case "examples": + dst.Examples = sibling.Examples + case "prefixItems": + dst.PrefixItems = sibling.PrefixItems + case "contains": + dst.Contains = sibling.Contains + case "minContains": + dst.MinContains = sibling.MinContains + case "maxContains": + dst.MaxContains = sibling.MaxContains + case "patternProperties": + dst.PatternProperties = sibling.PatternProperties + case "dependentSchemas": + dst.DependentSchemas = sibling.DependentSchemas + case "propertyNames": + dst.PropertyNames = sibling.PropertyNames + case "unevaluatedItems": + dst.UnevaluatedItems = sibling.UnevaluatedItems + case "unevaluatedProperties": + dst.UnevaluatedProperties = sibling.UnevaluatedProperties + case "if": + dst.If = sibling.If + case "then": + dst.Then = sibling.Then + case "else": + dst.Else = sibling.Else + case "dependentRequired": + dst.DependentRequired = sibling.DependentRequired + case "$defs": + dst.Defs = sibling.Defs + case "$schema": + dst.SchemaDialect = sibling.SchemaDialect + case "$comment": + dst.Comment = sibling.Comment + case "$id": + dst.SchemaID = sibling.SchemaID + case "$anchor": + dst.Anchor = sibling.Anchor + case "$dynamicRef": + dst.DynamicRef = sibling.DynamicRef + case "$dynamicAnchor": + dst.DynamicAnchor = sibling.DynamicAnchor + case "contentMediaType": + dst.ContentMediaType = sibling.ContentMediaType + case "contentEncoding": + dst.ContentEncoding = sibling.ContentEncoding + case "contentSchema": + dst.ContentSchema = sibling.ContentSchema + } + } } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/loader_uri_reader.go b/vendor/github.com/getkin/kin-openapi/openapi3/loader_uri_reader.go index b023dfb29..5dc4a9b7b 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/loader_uri_reader.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/loader_uri_reader.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" "os" + "path" "path/filepath" "sync" ) @@ -79,7 +80,7 @@ func ReadFromFile(loader *Loader, location *url.URL) ([]byte, error) { if !is_file(location) { return nil, ErrURINotSupported } - return os.ReadFile(filepath.FromSlash(location.Path)) + return os.ReadFile(path.Clean(filepath.FromSlash(location.Path))) } // URIMapCache returns a ReadFromURIFunc that caches the contents read from URI diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go b/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go index 35b336571..f651b52fd 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go @@ -2,7 +2,7 @@ package openapi3 import ( "encoding/json" - "sort" + "maps" "strings" "github.com/go-openapi/jsonpointer" @@ -16,6 +16,11 @@ func NewResponsesWithCapacity(cap int) *Responses { return &Responses{m: make(map[string]*ResponseRef, cap)} } +// Keys returns the responses keys in a fixed order +func (responses *Responses) Keys() []string { + return componentNames(responses.Map()) +} + // Value returns the responses for key or nil func (responses *Responses) Value(key string) *ResponseRef { if responses.Len() == 0 { @@ -55,9 +60,7 @@ func (responses *Responses) Map() (m map[string]*ResponseRef) { return make(map[string]*ResponseRef) } m = make(map[string]*ResponseRef, len(responses.m)) - for k, v := range responses.m { - m[k] = v - } + maps.Copy(m, responses.m) return } @@ -71,22 +74,19 @@ func (responses Responses) JSONLookup(token string) (any, error) { } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { - var vv *Response = v.Value - return vv, nil + return v.Value, nil } } // MarshalYAML returns the YAML encoding of Responses. func (responses *Responses) MarshalYAML() (any, error) { - if responses == nil { + if responses == nil || responses.isExplicitlyNull() { return nil, nil } m := make(map[string]any, responses.Len()+len(responses.Extensions)) - for k, v := range responses.Extensions { - m[k] = v - } - for k, v := range responses.Map() { - m[k] = v + maps.Copy(m, responses.Extensions) + for _, k := range responses.Keys() { + m[k] = responses.m[k] } return m, nil } @@ -107,35 +107,18 @@ func (responses *Responses) UnmarshalJSON(data []byte) (err error) { return } - ks := make([]string, 0, len(m)) - for k := range m { - ks = append(ks, k) - } - sort.Strings(ks) - x := Responses{ Extensions: make(map[string]any), m: make(map[string]*ResponseRef, len(m)), } - for _, k := range ks { + for _, k := range componentNames(m) { v := m[k] if strings.HasPrefix(k, "x-") { x.Extensions[k] = v continue } - if k == originKey { - var data []byte - if data, err = json.Marshal(v); err != nil { - return - } - if err = json.Unmarshal(data, &x.Origin); err != nil { - return - } - continue - } - var data []byte if data, err = json.Marshal(v); err != nil { return @@ -158,6 +141,11 @@ func NewCallbackWithCapacity(cap int) *Callback { return &Callback{m: make(map[string]*PathItem, cap)} } +// Keys returns the callback keys in a fixed order +func (callback *Callback) Keys() []string { + return componentNames(callback.Map()) +} + // Value returns the callback for key or nil func (callback *Callback) Value(key string) *PathItem { if callback.Len() == 0 { @@ -197,9 +185,7 @@ func (callback *Callback) Map() (m map[string]*PathItem) { return make(map[string]*PathItem) } m = make(map[string]*PathItem, len(callback.m)) - for k, v := range callback.m { - m[k] = v - } + maps.Copy(m, callback.m) return } @@ -213,8 +199,7 @@ func (callback Callback) JSONLookup(token string) (any, error) { } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { - var vv *PathItem = v - return vv, nil + return v, nil } } @@ -224,11 +209,9 @@ func (callback *Callback) MarshalYAML() (any, error) { return nil, nil } m := make(map[string]any, callback.Len()+len(callback.Extensions)) - for k, v := range callback.Extensions { - m[k] = v - } - for k, v := range callback.Map() { - m[k] = v + maps.Copy(m, callback.Extensions) + for _, k := range callback.Keys() { + m[k] = callback.m[k] } return m, nil } @@ -249,35 +232,18 @@ func (callback *Callback) UnmarshalJSON(data []byte) (err error) { return } - ks := make([]string, 0, len(m)) - for k := range m { - ks = append(ks, k) - } - sort.Strings(ks) - x := Callback{ Extensions: make(map[string]any), m: make(map[string]*PathItem, len(m)), } - for _, k := range ks { + for _, k := range componentNames(m) { v := m[k] if strings.HasPrefix(k, "x-") { x.Extensions[k] = v continue } - if k == originKey { - var data []byte - if data, err = json.Marshal(v); err != nil { - return - } - if err = json.Unmarshal(data, &x.Origin); err != nil { - return - } - continue - } - var data []byte if data, err = json.Marshal(v); err != nil { return @@ -300,6 +266,11 @@ func NewPathsWithCapacity(cap int) *Paths { return &Paths{m: make(map[string]*PathItem, cap)} } +// Keys returns the paths keys in a fixed order +func (paths *Paths) Keys() []string { + return componentNames(paths.Map()) +} + // Value returns the paths for key or nil func (paths *Paths) Value(key string) *PathItem { if paths.Len() == 0 { @@ -339,9 +310,7 @@ func (paths *Paths) Map() (m map[string]*PathItem) { return make(map[string]*PathItem) } m = make(map[string]*PathItem, len(paths.m)) - for k, v := range paths.m { - m[k] = v - } + maps.Copy(m, paths.m) return } @@ -355,8 +324,7 @@ func (paths Paths) JSONLookup(token string) (any, error) { } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { - var vv *PathItem = v - return vv, nil + return v, nil } } @@ -366,11 +334,9 @@ func (paths *Paths) MarshalYAML() (any, error) { return nil, nil } m := make(map[string]any, paths.Len()+len(paths.Extensions)) - for k, v := range paths.Extensions { - m[k] = v - } - for k, v := range paths.Map() { - m[k] = v + maps.Copy(m, paths.Extensions) + for _, k := range paths.Keys() { + m[k] = paths.m[k] } return m, nil } @@ -391,35 +357,18 @@ func (paths *Paths) UnmarshalJSON(data []byte) (err error) { return } - ks := make([]string, 0, len(m)) - for k := range m { - ks = append(ks, k) - } - sort.Strings(ks) - x := Paths{ Extensions: make(map[string]any), m: make(map[string]*PathItem, len(m)), } - for _, k := range ks { + for _, k := range componentNames(m) { v := m[k] if strings.HasPrefix(k, "x-") { x.Extensions[k] = v continue } - if k == originKey { - var data []byte - if data, err = json.Marshal(v); err != nil { - return - } - if err = json.Unmarshal(data, &x.Origin); err != nil { - return - } - continue - } - var data []byte if data, err = json.Marshal(v); err != nil { return diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go b/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go index 2f00828a6..6483fa9a4 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go @@ -3,6 +3,7 @@ package openapi3 import ( "encoding/json" "fmt" + "net/url" "strings" "github.com/oasdiff/yaml" @@ -16,7 +17,7 @@ func unmarshalError(jsonUnmarshalErr error) error { return jsonUnmarshalErr } -func unmarshal(data []byte, v any, includeOrigin bool) error { +func unmarshal(data []byte, v any, includeOrigin bool, location *url.URL) error { var jsonErr, yamlErr error // See https://github.com/getkin/kin-openapi/issues/680 @@ -25,8 +26,18 @@ func unmarshal(data []byte, v any, includeOrigin bool) error { } // UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys - if yamlErr = yaml.UnmarshalWithOrigin(data, v, includeOrigin); yamlErr == nil { + var file string + if location != nil { + file = location.String() + } + if tree, err := yaml.Unmarshal(data, v, yaml.DecodeOpts{ + Origin: yaml.OriginOpt{Enabled: includeOrigin, File: file}, + DisableTimestamps: true, + }); err == nil { + applyOrigins(v, tree) return nil + } else { + yamlErr = err } // If both unmarshaling attempts fail, return a new error that includes both errors diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go b/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go index 576826f37..842b320d6 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go @@ -3,9 +3,7 @@ package openapi3 import ( "context" "encoding/json" - "errors" - "fmt" - "sort" + "maps" "github.com/go-openapi/jsonpointer" ) @@ -14,12 +12,13 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#media-type-object type MediaType struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` - Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` - Example any `json:"example,omitempty" yaml:"example,omitempty"` - Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` - Encoding map[string]*Encoding `json:"encoding,omitempty" yaml:"encoding,omitempty"` + Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` + ItemSchema *SchemaRef `json:"itemSchema,omitempty" yaml:"itemSchema,omitempty"` // OpenAPI >=3.2 + Example any `json:"example,omitempty" yaml:"example,omitempty"` + Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` + Encoding Encodings `json:"encoding,omitempty" yaml:"encoding,omitempty"` } var _ jsonpointer.JSONPointable = (*MediaType)(nil) @@ -57,7 +56,7 @@ func (mediaType *MediaType) WithExample(name string, value any) *MediaType { func (mediaType *MediaType) WithEncoding(name string, enc *Encoding) *MediaType { encoding := mediaType.Encoding if encoding == nil { - encoding = make(map[string]*Encoding) + encoding = make(Encodings) mediaType.Encoding = encoding } encoding[name] = enc @@ -75,13 +74,14 @@ func (mediaType MediaType) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of MediaType. func (mediaType MediaType) MarshalYAML() (any, error) { - m := make(map[string]any, 4+len(mediaType.Extensions)) - for k, v := range mediaType.Extensions { - m[k] = v - } + m := make(map[string]any, 5+len(mediaType.Extensions)) + maps.Copy(m, mediaType.Extensions) if x := mediaType.Schema; x != nil { m["schema"] = x } + if x := mediaType.ItemSchema; x != nil { + m["itemSchema"] = x + } if x := mediaType.Example; x != nil { m["example"] = x } @@ -102,14 +102,15 @@ func (mediaType *MediaType) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "schema") + delete(x.Extensions, "itemSchema") delete(x.Extensions, "example") delete(x.Extensions, "examples") delete(x.Extensions, "encoding") if len(x.Extensions) == 0 { x.Extensions = nil } + delete(x.Encoding, originKey) *mediaType = MediaType(x) return nil } @@ -127,36 +128,41 @@ func (mediaType *MediaType) Validate(ctx context.Context, opts ...ValidationOpti } if mediaType.Example != nil && mediaType.Examples != nil { - return errors.New("example and examples are mutually exclusive") + return newMediaTypeExampleExamplesExclusive(mediaType.Origin) } if vo := getValidationOptions(ctx); !vo.examplesValidationDisabled { if example := mediaType.Example; example != nil { if err := validateExampleValue(ctx, example, schema.Value); err != nil { - return fmt.Errorf("invalid example: %w", err) + return newSchemaValueError("example", err, mediaType.Origin) } } if examples := mediaType.Examples; examples != nil { - names := make([]string, 0, len(examples)) - for name := range examples { - names = append(names, name) - } - sort.Strings(names) - for _, k := range names { + for _, k := range componentNames(examples) { v := examples[k] if err := v.Validate(ctx); err != nil { - return fmt.Errorf("example %s: %w", k, err) + return &MediaTypeExampleValidationError{ExampleName: k, Cause: err} } if err := validateExampleValue(ctx, v.Value.Value, schema.Value); err != nil { - return fmt.Errorf("example %s: %w", k, err) + return newSchemaValueError("example", + &MediaTypeExampleValidationError{ExampleName: k, Cause: err}, + exampleValueOrigin(v.Value, mediaType.Origin)) } } } } } + if itemSchema := mediaType.ItemSchema; itemSchema != nil { + if !getValidationOptions(ctx).isOpenAPI32OrLater { + return errFieldFor32Plus("itemSchema", mediaType.Origin) + } + if err := itemSchema.Validate(ctx); err != nil { + return err + } + } - return validateExtensions(ctx, mediaType.Extensions) + return validateExtensions(ctx, mediaType.Extensions, mediaType.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable @@ -171,6 +177,13 @@ func (mediaType MediaType) JSONLookup(token string) (any, error) { } case "example": return mediaType.Example, nil + case "itemSchema": + if mediaType.ItemSchema != nil { + if mediaType.ItemSchema.Ref != "" { + return &Ref{Ref: mediaType.ItemSchema.Ref}, nil + } + return mediaType.ItemSchema.Value, nil + } case "examples": return mediaType.Examples, nil case "encoding": diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go b/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go index ef1592e8c..287327fa2 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go @@ -3,29 +3,83 @@ package openapi3 import ( "context" "encoding/json" - "errors" "fmt" + "maps" "net/url" + "slices" "github.com/go-openapi/jsonpointer" ) // T is the root of an OpenAPI v3 document // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#openapi-object +// and https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#openapi-object type T struct { Extensions map[string]any `json:"-" yaml:"-"` + Origin *Origin `json:"-" yaml:"-"` - OpenAPI string `json:"openapi" yaml:"openapi"` // Required - Components *Components `json:"components,omitempty" yaml:"components,omitempty"` - Info *Info `json:"info" yaml:"info"` // Required - Paths *Paths `json:"paths" yaml:"paths"` // Required - Security SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"` - Servers Servers `json:"servers,omitempty" yaml:"servers,omitempty"` - Tags Tags `json:"tags,omitempty" yaml:"tags,omitempty"` - ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + OpenAPI string `json:"openapi" yaml:"openapi"` // Required + Components *Components `json:"components,omitempty" yaml:"components,omitempty"` + Info *Info `json:"info" yaml:"info"` // Required + Paths *Paths `json:"paths" yaml:"paths"` // Required in 3.0, optional in 3.1 + Security SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"` + Servers Servers `json:"servers,omitempty" yaml:"servers,omitempty"` + Tags Tags `json:"tags,omitempty" yaml:"tags,omitempty"` + ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + Webhooks map[string]*PathItem `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` // OpenAPI >=3.1 + JSONSchemaDialect string `json:"jsonSchemaDialect,omitempty" yaml:"jsonSchemaDialect,omitempty"` // OpenAPI >=3.1 visited visitedComponent url *url.URL + + // Document-scoped format validators + // These validators are automatically used by all schemas in this document + stringFormats map[string]StringFormatValidator + numberFormats map[string]NumberFormatValidator + integerFormats map[string]IntegerFormatValidator +} + +// IsOpenAPI30 returns whether doc is an OpenAPI document version 3.0.x. +// Returns true for 3, 3.0, 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.0.4, ... +// And false for 3.1.0, 3.2, ... and for invalid strings. +func (doc *T) IsOpenAPI30() bool { + return doc.OpenAPIMajorMinor() == "3.0" +} + +// IsOpenAPI31OrLater returns whether doc is an OpenAPI document version >=3.1. +// Returns true for 3.1, 3.1.0, 3.1.1, 3.1.2, 3.2.0, ... +// And false for cases where IsOpenAPI30 returns true and for invalid strings. +func (doc *T) IsOpenAPI31OrLater() bool { + return slices.Contains([]string{"3.1", "3.2"}, doc.OpenAPIMajorMinor()) +} + +// IsOpenAPI32OrLater returns whether doc is an OpenAPI document version >=3.2. +// Returns true for 3.2, 3.2.0, ... +// And false for cases where IsOpenAPI31OrLater returns true for 3.1.x and for invalid strings. +func (doc *T) IsOpenAPI32OrLater() bool { + return doc.OpenAPIMajorMinor() == "3.2" +} + +func errValueOfFieldFor31Plus(value any, field string) error { + return fmt.Errorf("value %q of field %s is for OpenAPI >=3.1", value, field) +} + +// OpenAPIMajorMinor returns 3.y of the OpenAPI "3.y" or "3.y.z" version of the document. +// Returns the empty string for invalid OpenAPI version strings. +func (doc *T) OpenAPIMajorMinor() string { + if doc == nil { + return "" + } + switch doc.OpenAPI { + case "3", "3.0", "3.0.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4": + return "3.0" + case "3.1", "3.1.0", "3.1.1", "3.1.2": + return "3.1" + case "3.2", "3.2.0": + return "3.2" + default: + return "" + } } var _ jsonpointer.JSONPointable = (*T)(nil) @@ -49,6 +103,10 @@ func (doc *T) JSONLookup(token string) (any, error) { return doc.Tags, nil case "externalDocs": return doc.ExternalDocs, nil + case "webhooks": + return doc.Webhooks, nil + case "jsonSchemaDialect": + return doc.JSONSchemaDialect, nil } v, _, err := jsonpointer.GetForToken(doc.Extensions, token) @@ -69,10 +127,8 @@ func (doc *T) MarshalYAML() (any, error) { if doc == nil { return nil, nil } - m := make(map[string]any, 4+len(doc.Extensions)) - for k, v := range doc.Extensions { - m[k] = v - } + m := make(map[string]any, 10+len(doc.Extensions)) + maps.Copy(m, doc.Extensions) m["openapi"] = doc.OpenAPI if x := doc.Components; x != nil { m["components"] = x @@ -91,6 +147,12 @@ func (doc *T) MarshalYAML() (any, error) { if x := doc.ExternalDocs; x != nil { m["externalDocs"] = x } + if x := doc.Webhooks; len(x) != 0 { + m["webhooks"] = x + } + if x := doc.JSONSchemaDialect; x != "" { + m["jsonSchemaDialect"] = x + } return m, nil } @@ -110,9 +172,12 @@ func (doc *T) UnmarshalJSON(data []byte) error { delete(x.Extensions, "servers") delete(x.Extensions, "tags") delete(x.Extensions, "externalDocs") + delete(x.Extensions, "webhooks") + delete(x.Extensions, "jsonSchemaDialect") if len(x.Extensions) == 0 { x.Extensions = nil } + delete(x.Webhooks, originKey) *doc = T(x) return nil } @@ -137,69 +202,196 @@ func (doc *T) AddServers(servers ...*Server) { doc.Servers = append(doc.Servers, servers...) } +// SetStringFormatValidators sets document-scoped string format validators. +// These validators are automatically used by all schemas in this document. +func (doc *T) SetStringFormatValidators(validators map[string]StringFormatValidator) { + doc.stringFormats = validators +} + +// SetStringFormatValidator sets a single document-scoped string format validator. +func (doc *T) SetStringFormatValidator(name string, validator StringFormatValidator) { + if doc.stringFormats == nil { + doc.stringFormats = make(map[string]StringFormatValidator) + } + doc.stringFormats[name] = validator +} + +// SetNumberFormatValidators sets document-scoped number format validators. +// These validators are automatically used by all schemas in this document. +func (doc *T) SetNumberFormatValidators(validators map[string]NumberFormatValidator) { + doc.numberFormats = validators +} + +// SetNumberFormatValidator sets a single document-scoped number format validator. +func (doc *T) SetNumberFormatValidator(name string, validator NumberFormatValidator) { + if doc.numberFormats == nil { + doc.numberFormats = make(map[string]NumberFormatValidator) + } + doc.numberFormats[name] = validator +} + +// SetIntegerFormatValidators sets document-scoped integer format validators. +// These validators are automatically used by all schemas in this document. +func (doc *T) SetIntegerFormatValidators(validators map[string]IntegerFormatValidator) { + doc.integerFormats = validators +} + +// SetIntegerFormatValidator sets a single document-scoped integer format validator. +func (doc *T) SetIntegerFormatValidator(name string, validator IntegerFormatValidator) { + if doc.integerFormats == nil { + doc.integerFormats = make(map[string]IntegerFormatValidator) + } + doc.integerFormats[name] = validator +} + +// GetSchemaValidationOptions returns SchemaValidationOptions that include +// this document's format validators. Use this when validating schemas from this document. +func (doc *T) GetSchemaValidationOptions() []SchemaValidationOption { + var opts []SchemaValidationOption + if doc.stringFormats != nil { + opts = append(opts, WithStringFormatValidators(doc.stringFormats)) + } + if doc.numberFormats != nil { + opts = append(opts, WithNumberFormatValidators(doc.numberFormats)) + } + if doc.integerFormats != nil { + opts = append(opts, WithIntegerFormatValidators(doc.integerFormats)) + } + return opts +} + // Validate returns an error if T does not comply with the OpenAPI spec. // Validations Options can be provided to modify the validation behavior. +// +// By default, doc.OpenAPI's field dictates whether "JSON Schema Draft 2020-12" validation +// is enabled. func (doc *T) Validate(ctx context.Context, opts ...ValidationOption) error { + if doc.IsOpenAPI31OrLater() { + opts = append(opts, IsOpenAPI31OrLater()) + } + if doc.IsOpenAPI32OrLater() { + opts = append(opts, IsOpenAPI32OrLater()) + } ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) if doc.OpenAPI == "" { - return errors.New("value of openapi must be a non-empty string") + if err := me.emit(newOpenAPIVersionRequired(doc.Origin)); err != nil { + return err + } + } + + if doc.Webhooks != nil && !doc.IsOpenAPI31OrLater() { + if err := me.emit(newWebhooksFieldFor31Plus(doc.Origin)); err != nil { + return err + } + } + if doc.JSONSchemaDialect != "" && !doc.IsOpenAPI31OrLater() { + if err := me.emit(newJSONSchemaDialectFieldFor31Plus(doc.Origin)); err != nil { + return err + } + } + + wrapSection := func(section string) func(error) error { + return func(e error) error { return &SectionValidationError{Section: section, Cause: e} } } var wrap func(error) error - wrap = func(e error) error { return fmt.Errorf("invalid components: %w", e) } + wrap = wrapSection("components") if v := doc.Components; v != nil { - if err := v.Validate(ctx); err != nil { - return wrap(err) + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { + return err } } - wrap = func(e error) error { return fmt.Errorf("invalid info: %w", e) } + wrap = wrapSection("info") if v := doc.Info; v != nil { - if err := v.Validate(ctx); err != nil { - return wrap(err) + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { + return err } - } else { - return wrap(errors.New("must be an object")) + } else if err := me.emit(wrap(newInfoRequired(doc.Origin))); err != nil { + return err } - wrap = func(e error) error { return fmt.Errorf("invalid paths: %w", e) } + wrap = wrapSection("paths") if v := doc.Paths; v != nil { - if err := v.Validate(ctx); err != nil { - return wrap(err) + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { + return err + } + } else if doc.IsOpenAPI30() { + if err := me.emit(wrap(newPathsRequired(doc.Origin))); err != nil { + return err } - } else { - return wrap(errors.New("must be an object")) } - wrap = func(e error) error { return fmt.Errorf("invalid security: %w", e) } + wrap = wrapSection("security") if v := doc.Security; v != nil { - if err := v.Validate(ctx); err != nil { - return wrap(err) + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { + return err } } - wrap = func(e error) error { return fmt.Errorf("invalid servers: %w", e) } + wrap = wrapSection("servers") if v := doc.Servers; v != nil { - if err := v.Validate(ctx); err != nil { - return wrap(err) + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { + return err } } - wrap = func(e error) error { return fmt.Errorf("invalid tags: %w", e) } + wrap = wrapSection("tags") if v := doc.Tags; v != nil { - if err := v.Validate(ctx); err != nil { - return wrap(err) + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { + return err } } - wrap = func(e error) error { return fmt.Errorf("invalid external docs: %w", e) } + wrap = wrapSection("external docs") if v := doc.ExternalDocs; v != nil { - if err := v.Validate(ctx); err != nil { - return wrap(err) + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { + return err + } + } + + wrap = wrapSection("webhooks") + for _, name := range componentNames(doc.Webhooks) { + pathItem := doc.Webhooks[name] + if pathItem == nil { + if err := me.emit(wrap(newWebhookNil(name))); err != nil { + return err + } + // Nothing to descend into for a nil webhook; the nil itself + // is the only finding under this name until the entry is + // populated, so continue to the next webhook. + continue + } + wrapWebhook := func(e error) error { return wrap(&WebhookValidationError{Name: name, Cause: e}) } + if err := me.emitWrapped(wrapWebhook, pathItem.Validate(ctx)); err != nil { + return err } } - return validateExtensions(ctx, doc.Extensions) + wrap = wrapSection("jsonSchemaDialect") + if doc.JSONSchemaDialect != "" { + u, err := url.Parse(doc.JSONSchemaDialect) + if err != nil { + if err = me.emit(wrap(err)); err != nil { + return err + } + } else if u.Scheme == "" { + if err := me.emit(wrap(newJSONSchemaDialectAbsoluteURIRequired(doc.Origin))); err != nil { + return err + } + } + } + + return me.finalize(validateExtensions(ctx, doc.Extensions, doc.Origin)) +} + +// ValidateSchemaJSON validates data against a schema using this document's format validators. +// This is a convenience method that automatically applies the document's format validators. +func (doc *T) ValidateSchemaJSON(schema *Schema, value any, opts ...SchemaValidationOption) error { + // Combine document's validators with any additional options + allOpts := append(doc.GetSchemaValidationOptions(), opts...) + return schema.VisitJSON(value, allOpts...) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/operation.go b/vendor/github.com/getkin/kin-openapi/openapi3/operation.go index f32836e68..512241095 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/operation.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/operation.go @@ -3,8 +3,7 @@ package openapi3 import ( "context" "encoding/json" - "errors" - "fmt" + "maps" "strconv" "github.com/go-openapi/jsonpointer" @@ -14,7 +13,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object type Operation struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` // Optional tags for documentation. Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` @@ -34,8 +33,8 @@ type Operation struct { // Optional body parameter. RequestBody *RequestBodyRef `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` - // Responses. - Responses *Responses `json:"responses" yaml:"responses"` // Required + // Responses are required in OpenAPI 3.0 and optional in OpenAPI 3.1 and later. + Responses *Responses `json:"responses" yaml:"responses"` // Optional callbacks Callbacks Callbacks `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` @@ -69,9 +68,7 @@ func (operation Operation) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Operation. func (operation Operation) MarshalYAML() (any, error) { m := make(map[string]any, 12+len(operation.Extensions)) - for k, v := range operation.Extensions { - m[k] = v - } + maps.Copy(m, operation.Extensions) if x := operation.Tags; len(x) != 0 { m["tags"] = x } @@ -90,7 +87,9 @@ func (operation Operation) MarshalYAML() (any, error) { if x := operation.RequestBody; x != nil { m["requestBody"] = x } - m["responses"] = operation.Responses + if x := operation.Responses; x != nil { + m["responses"] = x + } if x := operation.Callbacks; len(x) != 0 { m["callbacks"] = x } @@ -116,8 +115,11 @@ func (operation *Operation) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } + _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) + if value, present := x.Extensions["responses"]; present && value == nil { + x.Responses = &Responses{explicitlyNull: true} + } delete(x.Extensions, "tags") delete(x.Extensions, "summary") delete(x.Extensions, "description") @@ -193,32 +195,36 @@ func (operation *Operation) AddResponse(status int, response *Response) { // Validate returns an error if Operation does not comply with the OpenAPI spec. func (operation *Operation) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) if v := operation.Parameters; v != nil { - if err := v.Validate(ctx); err != nil { + if err := me.emit(v.Validate(ctx)); err != nil { return err } } if v := operation.RequestBody; v != nil { - if err := v.Validate(ctx); err != nil { + if err := me.emit(v.Validate(ctx)); err != nil { return err } } - if v := operation.Responses; v != nil { - if err := v.Validate(ctx); err != nil { + if v := operation.Responses; v != nil && !v.isExplicitlyNull() { + if err := me.emit(v.Validate(ctx)); err != nil { + return err + } + } else if v != nil || !getValidationOptions(ctx).isOpenAPI31OrLater { + if err := me.emit(newOperationResponsesRequired(operation.Origin)); err != nil { return err } - } else { - return errors.New("value of responses must be an object") } if v := operation.ExternalDocs; v != nil { - if err := v.Validate(ctx); err != nil { - return fmt.Errorf("invalid external docs: %w", err) + wrap := func(e error) error { return &SectionValidationError{Section: "external docs", Cause: e} } + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { + return err } } - return validateExtensions(ctx, operation.Extensions) + return me.finalize(validateExtensions(ctx, operation.Extensions, operation.Origin)) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/origin.go b/vendor/github.com/getkin/kin-openapi/openapi3/origin.go index d2a51d946..c6890e8ca 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/origin.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/origin.go @@ -1,17 +1,328 @@ package openapi3 +import ( + "reflect" + "sort" + "strings" + + "github.com/oasdiff/yaml" +) + const originKey = "__origin__" +var originPtrType = reflect.TypeFor[*Origin]() + // Origin contains the origin of a collection. // Key is the location of the collection itself. -// Fields is a map of the location of each field in the collection. +// Fields is a map of the location of each scalar field in the collection. +// Sequences is a map of the location of each item in sequence-valued fields. type Origin struct { - Key *Location `json:"key,omitempty" yaml:"key,omitempty"` - Fields map[string]Location `json:"fields,omitempty" yaml:"fields,omitempty"` + Key *Location `json:"key,omitempty" yaml:"key,omitempty"` + Fields map[string]Location `json:"fields,omitempty" yaml:"fields,omitempty"` + Sequences map[string][]Location `json:"sequences,omitempty" yaml:"sequences,omitempty"` } // Location is a struct that contains the location of a field. type Location struct { - Line int `json:"line,omitempty" yaml:"line,omitempty"` - Column int `json:"column,omitempty" yaml:"column,omitempty"` + File string `json:"file,omitempty" yaml:"file,omitempty"` + Line int `json:"line,omitempty" yaml:"line,omitempty"` + Column int `json:"column,omitempty" yaml:"column,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + + // EndLine and EndColumn mark the end of the block this location heads (set + // only on Origin.Key). For an operation or schema this spans the whole + // block, so a consumer can extract the entire element from its source. + // Both are zero when the underlying YAML carried no end information. + EndLine int `json:"endLine,omitempty" yaml:"endLine,omitempty"` + EndColumn int `json:"endColumn,omitempty" yaml:"endColumn,omitempty"` +} + +// originFromSeq parses the compact []any sequence produced by yaml3's addOrigin. +// +// Format: [file, key_name, key_line, key_col, nf, f1_name, f1_delta, f1_col, ..., ns, s1_name, s1_count, s1_l0_delta, s1_c0, ...] +func originFromSeq(s []any) *Origin { + // Need at least: file, key_name, key_line, key_col, nf, ns + if len(s) < 6 { + return nil + } + file, _ := s[0].(string) + keyName, _ := s[1].(string) + keyLine := toInt(s[2]) + keyCol := toInt(s[3]) + + o := &Origin{ + Key: &Location{ + File: file, + Line: keyLine, + Column: keyCol, + Name: keyName, + }, + } + + idx := 4 + nf := toInt(s[idx]) + idx++ + if nf > 0 && idx+nf*3 <= len(s) { + o.Fields = make(map[string]Location, nf) + for range nf { + fname, _ := s[idx].(string) + delta := toInt(s[idx+1]) + col := toInt(s[idx+2]) + o.Fields[fname] = Location{ + File: file, + Line: keyLine + delta, + Column: col, + Name: fname, + } + idx += 3 + } + } + + if idx >= len(s) { + return o + } + ns := toInt(s[idx]) + idx++ + if ns > 0 { + o.Sequences = make(map[string][]Location, ns) + for range ns { + if idx >= len(s) { + break + } + sname, _ := s[idx].(string) + idx++ + if idx >= len(s) { + break + } + count := toInt(s[idx]) + idx++ + locs := make([]Location, count) + for j := 0; j < count && idx+2 < len(s); j++ { + name, _ := s[idx].(string) + delta := toInt(s[idx+1]) + col := toInt(s[idx+2]) + locs[j] = Location{File: file, Line: keyLine + delta, Column: col, Name: name} + idx += 3 + } + o.Sequences[sname] = locs + } + } + + // Trailing block end (yaml3 >= the end-position release): end_delta, end_col. + // Reconstruct the end of the whole block on Origin.Key so a consumer can + // extract the entire element. Older origin sequences omit these, leaving + // EndLine/EndColumn zero. end_col == 0 means no end information was recorded. + if o.Key != nil && idx+1 < len(s) { + if endCol := toInt(s[idx+1]); endCol > 0 { + o.Key.EndLine = keyLine + toInt(s[idx]) + o.Key.EndColumn = endCol + } + } + return o +} + +// toInt converts numeric types to int. Handles int/uint64 from YAML decoding. +func toInt(v any) int { + switch n := v.(type) { + case int: + return n + case uint64: + return int(n) + } + return 0 +} + +// isScalarValuedMapField reports whether v is a non-empty map whose element +// type is a scalar (string, bool, or a numeric kind). Such a map decodes +// without an Origin field of its own, unlike a pointer- or struct-valued map +// whose elements each carry their own Origin. +func isScalarValuedMapField(v reflect.Value) bool { + if v.Kind() != reflect.Map || v.IsNil() || v.Len() == 0 { + return false + } + switch v.Type().Elem().Kind() { + case reflect.String, reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + return true + } + return false +} + +// recordMapKeyLocations copies the map-key locations from a scalar-valued map's +// own subtree onto parentOrigin.Sequences[field], so each key is addressable by +// name (the same shape used for sequence items). It is a no-op when the child +// carries no origin data. Keys are sorted for deterministic output. +func recordMapKeyLocations(parentOrigin *Origin, field string, childTree *yaml.OriginTree) { + s, ok := childTree.Origin.([]any) + if !ok { + return + } + childOrigin := originFromSeq(s) + if childOrigin == nil || len(childOrigin.Fields) == 0 { + return + } + locs := make([]Location, 0, len(childOrigin.Fields)) + for _, loc := range childOrigin.Fields { + locs = append(locs, loc) + } + sort.Slice(locs, func(i, j int) bool { return locs[i].Name < locs[j].Name }) + if parentOrigin.Sequences == nil { + parentOrigin.Sequences = make(map[string][]Location) + } + parentOrigin.Sequences[field] = locs +} + +// applyOrigins walks a Go struct tree and a parallel OriginTree, setting +// Origin fields on each struct from the extracted origin data. +func applyOrigins(v any, tree *yaml.OriginTree) { + if tree == nil { + return + } + applyOriginsToValue(reflect.ValueOf(v), tree) +} + +func applyOriginsToValue(val reflect.Value, tree *yaml.OriginTree) { + // Keep track of the last pointer so we can pass it to struct handlers + // (needed for calling methods like Map() on maplike types). + var ptr reflect.Value + for val.Kind() == reflect.Pointer || val.Kind() == reflect.Interface { + if val.IsNil() { + return + } + if val.Kind() == reflect.Pointer { + ptr = val + } + val = val.Elem() + } + + switch val.Kind() { + case reflect.Struct: + applyOriginsToStruct(val, ptr, tree) + case reflect.Map: + applyOriginsToMap(val, tree) + case reflect.Slice: + applyOriginsToSlice(val, tree) + } +} + +func applyOriginsToStruct(val reflect.Value, ptr reflect.Value, tree *yaml.OriginTree) { + typ := val.Type() + + // Set Origin field for structs whose Origin field has a "-" json tag. + var structOrigin *Origin + if tree.Origin != nil { + if sf, ok := typ.FieldByName("Origin"); ok && sf.Type == originPtrType { + tag := sf.Tag.Get("json") + if tag == "-" { + if s, ok := tree.Origin.([]any); ok { + structOrigin = originFromSeq(s) + val.FieldByName("Origin").Set(reflect.ValueOf(structOrigin)) + } + } + } + } + + // Recurse into exported struct fields using json tags + for i := range typ.NumField() { + sf := typ.Field(i) + if !sf.IsExported() { + continue + } + tag := jsonTagName(sf) + if tag == "" || tag == "-" { + continue + } + childTree := tree.Fields[tag] + if childTree == nil { + continue + } + // A scalar-valued map (e.g. OAuth scopes: map[string]string) decodes into + // a Go map that has no Origin field of its own, so its per-key locations — + // present in the child subtree — would otherwise be lost. Record them on + // this struct's Origin as a named sequence so a consumer can locate each + // entry by key. Object- or pointer-valued maps are excluded: their values + // carry their own Origin via the recursion below. + if structOrigin != nil && isScalarValuedMapField(val.Field(i)) { + recordMapKeyLocations(structOrigin, tag, childTree) + } + applyOriginsToValue(val.Field(i), childTree) + } + + // Handle wrapper types whose inner struct has no json tag: + // - *Ref types (e.g. SchemaRef, ResponseRef) have a "Value" field + // - BoolSchema (AdditionalProperties, UnevaluatedProperties, UnevaluatedItems) has a "Schema" field + // The origin tree data applies to the inner struct, not a sub-key. + for _, fieldName := range []string{"Value", "Schema"} { + vf := val.FieldByName(fieldName) + if !vf.IsValid() || vf.Kind() != reflect.Pointer || vf.IsNil() { + continue + } + sf, _ := typ.FieldByName(fieldName) + if sf.Tag.Get("json") == "" { + applyOriginsToValue(vf, tree) + } + } + + // Handle "maplike" types (Paths, Responses, Callback) whose items are + // stored in an unexported map accessible via a Map() method. + // Use the original pointer (if available) since dereferenced values + // are not addressable. + receiver := val + if ptr.IsValid() { + receiver = ptr + } else if val.CanAddr() { + receiver = val.Addr() + } + if receiver.Kind() == reflect.Pointer { + if mapMethod := receiver.MethodByName("Map"); mapMethod.IsValid() { + results := mapMethod.Call(nil) + if len(results) == 1 { + applyOriginsToMap(results[0], tree) + } + } + } +} + +func applyOriginsToMap(val reflect.Value, tree *yaml.OriginTree) { + if tree.Fields == nil { + return + } + for _, key := range val.MapKeys() { + childTree := tree.Fields[key.String()] + if childTree == nil { + continue + } + elem := val.MapIndex(key) + // Map values are not addressable. For pointer-typed values we can + // recurse directly. For value types we must copy, apply, and set back. + if elem.Kind() == reflect.Pointer || elem.Kind() == reflect.Interface { + applyOriginsToValue(elem, childTree) + } else if elem.Kind() == reflect.Struct { + // Copy to a settable value + cp := reflect.New(elem.Type()).Elem() + cp.Set(elem) + applyOriginsToStruct(cp, reflect.Value{}, childTree) + val.SetMapIndex(key, cp) + } + } +} + +func applyOriginsToSlice(val reflect.Value, tree *yaml.OriginTree) { + for i := 0; i < val.Len() && i < len(tree.Items); i++ { + if tree.Items[i] != nil { + applyOriginsToValue(val.Index(i), tree.Items[i]) + } + } +} + +// jsonTagName returns the JSON field name from a struct field's json tag. +func jsonTagName(f reflect.StructField) string { + tag := f.Tag.Get("json") + if tag == "" { + return "" + } + name, _, _ := strings.Cut(tag, ",") + return name } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go b/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go index 23582f7df..b1010a61e 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go @@ -3,9 +3,8 @@ package openapi3 import ( "context" "encoding/json" - "errors" "fmt" - "sort" + "maps" "strconv" "github.com/go-openapi/jsonpointer" @@ -57,7 +56,7 @@ func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOpt if v := parameterRef.Value; v != nil { key := v.In + ":" + v.Name if _, ok := dupes[key]; ok { - return fmt.Errorf("more than one %q parameter has name %q", v.In, v.Name) + return newDuplicateParameter(v.In, v.Name, v.Origin) } dupes[key] = struct{}{} } @@ -73,7 +72,7 @@ func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOpt // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object type Parameter struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Name string `json:"name,omitempty" yaml:"name,omitempty"` In string `json:"in,omitempty" yaml:"in,omitempty"` @@ -161,9 +160,7 @@ func (parameter Parameter) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Parameter. func (parameter Parameter) MarshalYAML() (any, error) { m := make(map[string]any, 13+len(parameter.Extensions)) - for k, v := range parameter.Extensions { - m[k] = v - } + maps.Copy(m, parameter.Extensions) if x := parameter.Name; x != "" { m["name"] = x @@ -217,7 +214,6 @@ func (parameter *Parameter) UnmarshalJSON(data []byte) error { } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "in") delete(x.Extensions, "description") @@ -314,7 +310,7 @@ func (parameter *Parameter) Validate(ctx context.Context, opts ...ValidationOpti ctx = WithValidationOptions(ctx, opts...) if parameter.Name == "" { - return errors.New("parameter name can't be blank") + return newParameterNameRequired(parameter.Origin) } in := parameter.In switch in { @@ -324,11 +320,11 @@ func (parameter *Parameter) Validate(ctx context.Context, opts ...ValidationOpti ParameterInHeader, ParameterInCookie: default: - return fmt.Errorf("parameter can't have 'in' value %q", parameter.In) + return newInvalidParameterIn(parameter.In, parameter.Origin) } if in == ParameterInPath && !parameter.Required { - return fmt.Errorf("path parameter %q must be required", parameter.Name) + return newPathParameterRequired(parameter.Name, parameter.Origin) } // Validate a parameter's serialization method. @@ -361,32 +357,32 @@ func (parameter *Parameter) Validate(ctx context.Context, opts ...ValidationOpti smSupported = true } if !smSupported { - e := fmt.Errorf("serialization method with style=%q and explode=%v is not supported by a %s parameter", sm.Style, sm.Explode, in) - return fmt.Errorf("parameter %q schema is invalid: %w", parameter.Name, e) + e := newInvalidSerializationMethod(in, sm.Style, sm.Explode, parameter.Origin) + return &ParameterFieldValidationError{ParameterName: parameter.Name, Field: "schema", Cause: e} } if (parameter.Schema == nil) == (len(parameter.Content) == 0) { - e := errors.New("parameter must contain exactly one of content and schema") - return fmt.Errorf("parameter %q schema is invalid: %w", parameter.Name, e) + return &ParameterFieldValidationError{ParameterName: parameter.Name, Field: "schema", + Cause: newParameterContentSchemaExactlyOne(parameter.Origin)} } if content := parameter.Content; content != nil { - e := errors.New("parameter content must only contain one entry") if len(content) > 1 { - return fmt.Errorf("parameter %q content is invalid: %w", parameter.Name, e) + return &ParameterFieldValidationError{ParameterName: parameter.Name, Field: "content", + Cause: newParameterContentSingleEntry(parameter.Origin)} } if err := content.Validate(ctx); err != nil { - return fmt.Errorf("parameter %q content is invalid: %w", parameter.Name, err) + return &ParameterFieldValidationError{ParameterName: parameter.Name, Field: "content", Cause: err} } } if schema := parameter.Schema; schema != nil { if err := schema.Validate(ctx); err != nil { - return fmt.Errorf("parameter %q schema is invalid: %w", parameter.Name, err) + return &ParameterFieldValidationError{ParameterName: parameter.Name, Field: "schema", Cause: err} } if parameter.Example != nil && parameter.Examples != nil { - return fmt.Errorf("parameter %q example and examples are mutually exclusive", parameter.Name) + return newParameterExampleAndExamplesExclusive(parameter.Name, parameter.Origin) } if vo := getValidationOptions(ctx); vo.examplesValidationDisabled { @@ -394,31 +390,28 @@ func (parameter *Parameter) Validate(ctx context.Context, opts ...ValidationOpti } if example := parameter.Example; example != nil { if err := validateExampleValue(ctx, example, schema.Value); err != nil { - return fmt.Errorf("invalid example: %w", err) + return newSchemaValueError("example", err, parameter.Origin) } } else if examples := parameter.Examples; examples != nil { - names := make([]string, 0, len(examples)) - for name := range examples { - names = append(names, name) - } - sort.Strings(names) - for _, k := range names { + for _, k := range componentNames(examples) { v := examples[k] if err := v.Validate(ctx); err != nil { - return fmt.Errorf("%s: %w", k, err) + return &ParameterExampleValidationError{ExampleName: k, Cause: err} } if err := validateExampleValue(ctx, v.Value.Value, schema.Value); err != nil { - return fmt.Errorf("%s: %w", k, err) + return newSchemaValueError("example", + &ParameterExampleValidationError{ExampleName: k, Cause: err}, + exampleValueOrigin(v.Value, parameter.Origin)) } } } } - return validateExtensions(ctx, parameter.Extensions) + return validateExtensions(ctx, parameter.Extensions, parameter.Origin) } // UnmarshalJSON sets ParametersMap to a copy of data. func (parametersMap *ParametersMap) UnmarshalJSON(data []byte) (err error) { - *parametersMap, _, err = unmarshalStringMapP[ParameterRef](data) + *parametersMap, err = unmarshalStringMapP[ParameterRef](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/path_item.go b/vendor/github.com/getkin/kin-openapi/openapi3/path_item.go index 983978616..701d9911a 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/path_item.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/path_item.go @@ -4,15 +4,15 @@ import ( "context" "encoding/json" "fmt" + "maps" "net/http" - "sort" ) // PathItem is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#path-item-object type PathItem struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` @@ -46,9 +46,7 @@ func (pathItem PathItem) MarshalYAML() (any, error) { } m := make(map[string]any, 13+len(pathItem.Extensions)) - for k, v := range pathItem.Extensions { - m[k] = v - } + maps.Copy(m, pathItem.Extensions) if x := pathItem.Summary; x != "" { m["summary"] = x } @@ -99,7 +97,6 @@ func (pathItem *PathItem) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "$ref") delete(x.Extensions, "summary") delete(x.Extensions, "description") @@ -206,28 +203,25 @@ func (pathItem *PathItem) SetOperation(method string, operation *Operation) { // Validate returns an error if PathItem does not comply with the OpenAPI spec. func (pathItem *PathItem) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) operations := pathItem.Operations() - methods := make([]string, 0, len(operations)) - for method := range operations { - methods = append(methods, method) - } - sort.Strings(methods) - for _, method := range methods { + for _, method := range componentNames(operations) { operation := operations[method] - if err := operation.Validate(ctx); err != nil { - return fmt.Errorf("invalid operation %s: %v", method, err) + wrapOp := func(e error) error { return &OperationValidationError{Method: method, Cause: e} } + if err := me.emitWrapped(wrapOp, operation.Validate(ctx)); err != nil { + return err } } if v := pathItem.Parameters; v != nil { - if err := v.Validate(ctx); err != nil { + if err := me.emit(v.Validate(ctx)); err != nil { return err } } - return validateExtensions(ctx, pathItem.Extensions) + return me.finalize(validateExtensions(ctx, pathItem.Extensions, pathItem.Origin)) } // isEmpty's introduced in 546590b1 diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/paths.go b/vendor/github.com/getkin/kin-openapi/openapi3/paths.go index edd8c8787..048a82403 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/paths.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/paths.go @@ -1,9 +1,9 @@ package openapi3 import ( + "cmp" "context" - "fmt" - "sort" + "slices" "strings" ) @@ -11,7 +11,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#paths-object type Paths struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` m map[string]*PathItem } @@ -40,18 +40,20 @@ func WithPath(path string, pathItem *PathItem) NewPathsOption { // Validate returns an error if Paths does not comply with the OpenAPI spec. func (paths *Paths) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) normalizedPaths := make(map[string]string, paths.Len()) - keys := make([]string, 0, paths.Len()) - for key := range paths.Map() { - keys = append(keys, key) - } - sort.Strings(keys) - for _, path := range keys { + for _, path := range paths.Keys() { pathItem := paths.Value(path) if path == "" || path[0] != '/' { - return fmt.Errorf("path %q does not start with a forward slash (/)", path) + if err := me.emit(newPathMustStartWithSlash(path, paths.Origin)); err != nil { + return err + } + // Skip validating operations under a malformed path key: any + // findings below would be addressed under a path that has no + // resolution path until the key itself is fixed. + continue } if pathItem == nil { @@ -61,9 +63,16 @@ func (paths *Paths) Validate(ctx context.Context, opts ...ValidationOption) erro normalizedPath, _, varsInPath := normalizeTemplatedPath(path) if oldPath, ok := normalizedPaths[normalizedPath]; ok { - return fmt.Errorf("conflicting paths %q and %q", path, oldPath) + if err := me.emit(newConflictingPaths(path, oldPath, paths.Origin)); err != nil { + return err + } + // Skip validating operations under a duplicate path: the + // first occurrence already validated its operations under the + // canonical path, so re-running would surface duplicate-but- + // identical findings without new information. + continue } - normalizedPaths[path] = path + normalizedPaths[normalizedPath] = path var commonParams []string for _, parameterRef := range pathItem.Parameters { @@ -74,12 +83,7 @@ func (paths *Paths) Validate(ctx context.Context, opts ...ValidationOption) erro } } operations := pathItem.Operations() - methods := make([]string, 0, len(operations)) - for method := range operations { - methods = append(methods, method) - } - sort.Strings(methods) - for _, method := range methods { + for _, method := range componentNames(operations) { operation := operations[method] var setParams []string for _, parameterRef := range operation.Parameters { @@ -101,38 +105,36 @@ func (paths *Paths) Validate(ctx context.Context, opts ...ValidationOption) erro missing[name] = struct{}{} } } - for name := range varsInPath { - got := false - for _, othername := range definedParams { - if othername == name { - got = true - break - } - } - if !got { - missing[name] = struct{}{} + for _, name := range componentNames(varsInPath) { + if slices.Contains(definedParams, name) { + break } + missing[name] = struct{}{} } if len(missing) != 0 { - missings := make([]string, 0, len(missing)) - for name := range missing { - missings = append(missings, name) + if err := me.emit(&PathParametersError{ + Path: path, + Method: method, + Missing: componentNames(missing), + Origin: pathItem.Origin, + }); err != nil { + return err } - return fmt.Errorf("operation %s %s must define exactly all path parameters (missing: %v)", method, path, missings) } } } - if err := pathItem.Validate(ctx); err != nil { - return fmt.Errorf("invalid path %s: %v", path, err) + wrapPath := func(e error) error { return &PathValidationError{Path: path, Cause: e} } + if err := me.emitWrapped(wrapPath, pathItem.Validate(ctx)); err != nil { + return err } } - if err := paths.validateUniqueOperationIDs(); err != nil { + if err := me.emit(paths.validateUniqueOperationIDs()); err != nil { return err } - return validateExtensions(ctx, paths.Extensions) + return me.finalize(validateExtensions(ctx, paths.Extensions, paths.Origin)) } // InMatchingOrder returns paths in the order they are matched against URLs. @@ -159,7 +161,7 @@ func (paths *Paths) InMatchingOrder() []string { ordered := make([]string, 0, paths.Len()) for c := 0; c <= max; c++ { if ps, ok := vars[c]; ok { - sort.Sort(sort.Reverse(sort.StringSlice(ps))) + slices.SortFunc(ps, func(a, b string) int { return cmp.Compare(b, a) }) ordered = append(ordered, ps...) } } @@ -186,10 +188,11 @@ func (paths *Paths) Find(key string) *PathItem { } normalizedPath, expected, _ := normalizeTemplatedPath(key) - for path, pathItem := range paths.Map() { + pathsMap := paths.Map() + for _, path := range componentNames(pathsMap) { pathNormalized, got, _ := normalizeTemplatedPath(path) if got == expected && pathNormalized == normalizedPath { - return pathItem + return pathsMap[path] } } return nil @@ -197,11 +200,15 @@ func (paths *Paths) Find(key string) *PathItem { func (paths *Paths) validateUniqueOperationIDs() error { operationIDs := make(map[string]string) - for urlPath, pathItem := range paths.Map() { + pathsMap := paths.Map() + for _, urlPath := range componentNames(pathsMap) { + pathItem := pathsMap[urlPath] if pathItem == nil { continue } - for httpMethod, operation := range pathItem.Operations() { + operations := pathItem.Operations() + for _, httpMethod := range componentNames(operations) { + operation := operations[httpMethod] if operation == nil || operation.OperationID == "" { continue } @@ -210,8 +217,7 @@ func (paths *Paths) validateUniqueOperationIDs() error { if endpoint > endpointDup { // For make error message a bit more deterministic. May be useful for tests. endpoint, endpointDup = endpointDup, endpoint } - return fmt.Errorf("operations %q and %q have the same operation id %q", - endpoint, endpointDup, operation.OperationID) + return newDuplicateOperationID(endpoint, endpointDup, operation.OperationID, operation.Origin) } operationIDs[operation.OperationID] = endpoint } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/ref.go b/vendor/github.com/getkin/kin-openapi/openapi3/ref.go index 83893b69a..095178dfe 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/ref.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/ref.go @@ -1,10 +1,42 @@ package openapi3 +import ( + "context" + "encoding/json" + "maps" +) + //go:generate go run refsgenerator.go // Ref is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object type Ref struct { - Ref string `json:"$ref" yaml:"$ref"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Ref string `json:"$ref" yaml:"$ref"` + Extensions map[string]any `json:"-" yaml:"-"` + Origin *Origin `json:"-" yaml:"-"` +} + +// MarshalYAML returns the YAML encoding of Ref. +func (x Ref) MarshalYAML() (any, error) { + m := make(map[string]any, 1+len(x.Extensions)) + maps.Copy(m, x.Extensions) + if x := x.Ref; x != "" { + m["$ref"] = x + } + return m, nil +} + +// MarshalJSON returns the JSON encoding of Ref. +func (x Ref) MarshalJSON() ([]byte, error) { + y, err := x.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(y) +} + +// Validate returns an error if Extensions does not comply with the OpenAPI spec. +func (e *Ref) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + return validateExtensions(ctx, e.Extensions, e.Origin) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/refs.go b/vendor/github.com/getkin/kin-openapi/openapi3/refs.go index fc11164a5..2a4066f63 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/refs.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/refs.go @@ -1,16 +1,13 @@ -// Code generated by go generate; DO NOT EDIT. +// Code generated by go generate using refs.tmpl; DO NOT EDIT refs.go. package openapi3 import ( "context" "encoding/json" - "fmt" "net/url" - "sort" "strings" "github.com/go-openapi/jsonpointer" - "github.com/perimeterx/marshmallow" ) // CallbackRef represents either a Callback or a $ref to a Callback. @@ -19,7 +16,7 @@ type CallbackRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *Callback @@ -54,7 +51,7 @@ func (x *CallbackRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of CallbackRef. func (x CallbackRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -71,15 +68,14 @@ func (x CallbackRef) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets CallbackRef to a copy of data. func (x *CallbackRef) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) - } - sort.Strings(x.extra) + x.extra = componentNames(extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -94,47 +90,52 @@ func (x *CallbackRef) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if CallbackRef does not comply with the OpenAPI spec. -func (x *CallbackRef) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if CallbackRef has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *CallbackRef) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + return newExtraSiblingFields(extras, x.Origin) + } + return nil +} + +// Validate returns an error if CallbackRef does not comply with the OpenAPI spec. +func (x *CallbackRef) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable @@ -157,7 +158,7 @@ type ExampleRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *Example @@ -192,7 +193,7 @@ func (x *ExampleRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of ExampleRef. func (x ExampleRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -209,15 +210,14 @@ func (x ExampleRef) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets ExampleRef to a copy of data. func (x *ExampleRef) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) - } - sort.Strings(x.extra) + x.extra = componentNames(extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -232,47 +232,52 @@ func (x *ExampleRef) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if ExampleRef does not comply with the OpenAPI spec. -func (x *ExampleRef) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if ExampleRef has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *ExampleRef) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + return newExtraSiblingFields(extras, x.Origin) + } + return nil +} + +// Validate returns an error if ExampleRef does not comply with the OpenAPI spec. +func (x *ExampleRef) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable @@ -295,7 +300,7 @@ type HeaderRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *Header @@ -330,7 +335,7 @@ func (x *HeaderRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of HeaderRef. func (x HeaderRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -347,15 +352,14 @@ func (x HeaderRef) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets HeaderRef to a copy of data. func (x *HeaderRef) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) - } - sort.Strings(x.extra) + x.extra = componentNames(extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -370,47 +374,52 @@ func (x *HeaderRef) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if HeaderRef does not comply with the OpenAPI spec. -func (x *HeaderRef) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if HeaderRef has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *HeaderRef) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + return newExtraSiblingFields(extras, x.Origin) + } + return nil +} + +// Validate returns an error if HeaderRef does not comply with the OpenAPI spec. +func (x *HeaderRef) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable @@ -433,7 +442,7 @@ type LinkRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *Link @@ -468,7 +477,7 @@ func (x *LinkRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of LinkRef. func (x LinkRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -485,15 +494,14 @@ func (x LinkRef) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets LinkRef to a copy of data. func (x *LinkRef) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) - } - sort.Strings(x.extra) + x.extra = componentNames(extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -508,47 +516,52 @@ func (x *LinkRef) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if LinkRef does not comply with the OpenAPI spec. -func (x *LinkRef) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if LinkRef has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *LinkRef) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + return newExtraSiblingFields(extras, x.Origin) + } + return nil +} + +// Validate returns an error if LinkRef does not comply with the OpenAPI spec. +func (x *LinkRef) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable @@ -571,7 +584,7 @@ type ParameterRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *Parameter @@ -606,7 +619,7 @@ func (x *ParameterRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of ParameterRef. func (x ParameterRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -623,15 +636,14 @@ func (x ParameterRef) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets ParameterRef to a copy of data. func (x *ParameterRef) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) - } - sort.Strings(x.extra) + x.extra = componentNames(extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -646,47 +658,52 @@ func (x *ParameterRef) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if ParameterRef does not comply with the OpenAPI spec. -func (x *ParameterRef) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if ParameterRef has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *ParameterRef) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + return newExtraSiblingFields(extras, x.Origin) + } + return nil +} + +// Validate returns an error if ParameterRef does not comply with the OpenAPI spec. +func (x *ParameterRef) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable @@ -709,7 +726,7 @@ type RequestBodyRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *RequestBody @@ -744,7 +761,7 @@ func (x *RequestBodyRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of RequestBodyRef. func (x RequestBodyRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -761,15 +778,14 @@ func (x RequestBodyRef) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets RequestBodyRef to a copy of data. func (x *RequestBodyRef) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) - } - sort.Strings(x.extra) + x.extra = componentNames(extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -784,47 +800,52 @@ func (x *RequestBodyRef) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if RequestBodyRef does not comply with the OpenAPI spec. -func (x *RequestBodyRef) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if RequestBodyRef has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *RequestBodyRef) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + return newExtraSiblingFields(extras, x.Origin) + } + return nil +} + +// Validate returns an error if RequestBodyRef does not comply with the OpenAPI spec. +func (x *RequestBodyRef) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable @@ -847,7 +868,7 @@ type ResponseRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *Response @@ -882,7 +903,7 @@ func (x *ResponseRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of ResponseRef. func (x ResponseRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -899,15 +920,14 @@ func (x ResponseRef) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets ResponseRef to a copy of data. func (x *ResponseRef) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) - } - sort.Strings(x.extra) + x.extra = componentNames(extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -922,47 +942,52 @@ func (x *ResponseRef) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if ResponseRef does not comply with the OpenAPI spec. -func (x *ResponseRef) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if ResponseRef has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *ResponseRef) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + return newExtraSiblingFields(extras, x.Origin) + } + return nil +} + +// Validate returns an error if ResponseRef does not comply with the OpenAPI spec. +func (x *ResponseRef) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable @@ -985,11 +1010,14 @@ type SchemaRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *Schema extra []string + // sibling holds keyword siblings of a $ref (OAS 3.1 / JSON Schema 2020-12). + // It is populated during unmarshal and applied to Value after $ref resolution. + sibling *Schema refPath *url.URL } @@ -1020,7 +1048,7 @@ func (x *SchemaRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of SchemaRef. func (x SchemaRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -1037,15 +1065,30 @@ func (x SchemaRef) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets SchemaRef to a copy of data. func (x *SchemaRef) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) + x.extra = componentNames(extra) + // OAS 3.1 / JSON Schema 2020-12: sibling keywords alongside $ref are valid + // and must be merged with the resolved reference. Parse the full object so + // the sibling fields are available after $ref resolution in resolveSchemaRef. + hasSiblings := false + for k := range extra { + if !strings.HasPrefix(k, "x-") { + hasSiblings = true + break + } + } + if hasSiblings { + var sibling Schema + if err := json.Unmarshal(data, &sibling); err == nil { + x.sibling = &sibling + } } - sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -1060,47 +1103,54 @@ func (x *SchemaRef) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if SchemaRef does not comply with the OpenAPI spec. -func (x *SchemaRef) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if SchemaRef has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *SchemaRef) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + if !validationOpts.isOpenAPI31OrLater { + return newExtraSiblingFields(extras, x.Origin) + } + } + return nil +} + +// Validate returns an error if SchemaRef does not comply with the OpenAPI spec. +func (x *SchemaRef) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable @@ -1123,7 +1173,7 @@ type SecuritySchemeRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *SecurityScheme @@ -1158,7 +1208,7 @@ func (x *SecuritySchemeRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of SecuritySchemeRef. func (x SecuritySchemeRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -1175,15 +1225,14 @@ func (x SecuritySchemeRef) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets SecuritySchemeRef to a copy of data. func (x *SecuritySchemeRef) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) - } - sort.Strings(x.extra) + x.extra = componentNames(extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -1198,47 +1247,52 @@ func (x *SecuritySchemeRef) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if SecuritySchemeRef does not comply with the OpenAPI spec. -func (x *SecuritySchemeRef) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if SecuritySchemeRef has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *SecuritySchemeRef) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + return newExtraSiblingFields(extras, x.Origin) + } + return nil +} + +// Validate returns an error if SecuritySchemeRef does not comply with the OpenAPI spec. +func (x *SecuritySchemeRef) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl b/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl index 028deba7d..fdff1c968 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl +++ b/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl @@ -1,16 +1,13 @@ -// Code generated by go generate; DO NOT EDIT. +// Code generated by go generate using refs.tmpl; DO NOT EDIT refs.go. package {{ .Package }} import ( "context" "encoding/json" - "fmt" "net/url" - "sort" "strings" "github.com/go-openapi/jsonpointer" - "github.com/perimeterx/marshmallow" ) {{ range $type := .Types }} // {{ $type.Name }}Ref represents either a {{ $type.Name }} or a $ref to a {{ $type.Name }}. @@ -19,11 +16,16 @@ type {{ $type.Name }}Ref struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any - Origin *Origin + Origin *Origin `json:"-" yaml:"-"` Ref string Value *{{ $type.Name }} extra []string +{{- if eq $type.Name "Schema" }} + // sibling holds keyword siblings of a $ref (OAS 3.1 / JSON Schema 2020-12). + // It is populated during unmarshal and applied to Value after $ref resolution. + sibling *Schema +{{- end }} refPath *url.URL } @@ -54,7 +56,7 @@ func (x *{{ $type.Name }}Ref) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of {{ $type.Name }}Ref. func (x {{ $type.Name }}Ref) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref}, nil + return &Ref{Ref: ref, Extensions: x.Extensions}, nil } return x.Value.MarshalYAML() } @@ -71,15 +73,32 @@ func (x {{ $type.Name }}Ref) MarshalJSON() ([]byte, error) { // UnmarshalJSON sets {{ $type.Name }}Ref to a copy of data. func (x *{{ $type.Name }}Ref) UnmarshalJSON(data []byte) error { var refOnly Ref - if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + if err := json.Unmarshal(data, &refOnly); err == nil && refOnly.Ref != "" { + extra := map[string]any{} + _ = json.Unmarshal(data, &extra) + delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { - x.extra = make([]string, 0, len(extra)) - for key := range extra { - x.extra = append(x.extra, key) + x.extra = componentNames(extra) +{{- if eq $type.Name "Schema" }} + // OAS 3.1 / JSON Schema 2020-12: sibling keywords alongside $ref are valid + // and must be merged with the resolved reference. Parse the full object so + // the sibling fields are available after $ref resolution in resolveSchemaRef. + hasSiblings := false + for k := range extra { + if !strings.HasPrefix(k, "x-") { + hasSiblings = true + break + } } - sort.Strings(x.extra) + if hasSiblings { + var sibling Schema + if err := json.Unmarshal(data, &sibling); err == nil { + x.sibling = &sibling + } + } +{{- end }} for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) @@ -94,47 +113,58 @@ func (x *{{ $type.Name }}Ref) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &x.Value) } -// Validate returns an error if {{ $type.Name }}Ref does not comply with the OpenAPI spec. -func (x *{{ $type.Name }}Ref) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) - exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited +// validateExtras returns an error if {{ $type.Name }}Ref has sibling fields +// alongside $ref that are not allowed by the validation options. +func (x *{{ $type.Name }}Ref) validateExtras(ctx context.Context) error { + validationOpts := getValidationOptions(ctx) var extras []string - if extra := x.extra; len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for _, ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } - } - // extras in the Extensions checked below + + allowed := validationOpts.extraSiblingFieldsAllowed + if allowed == nil { + allowed = make(map[string]struct{}) + } + for _, ex := range x.extra { + if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } + // extras in the Extensions checked below } } - if extra := x.Extensions; exProhibited && len(extra) != 0 { - allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed - for ex := range extra { - if allowed != nil { - if _, ok := allowed[ex]; ok { - continue - } + if validationOpts.schemaExtensionsInRefProhibited { + for _, ex := range componentNames(x.Extensions) { + if _, ok := allowed[ex]; !ok { + extras = append(extras, ex) } - extras = append(extras, ex) + // extras in the Extensions checked below } } if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) +{{- if eq $type.Name "Schema" }} + if !validationOpts.isOpenAPI31OrLater { + return newExtraSiblingFields(extras, x.Origin) + } +{{- else }} + return newExtraSiblingFields(extras, x.Origin) +{{- end }} + } + return nil +} + +// Validate returns an error if {{ $type.Name }}Ref does not comply with the OpenAPI spec. +func (x *{{ $type.Name }}Ref) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + if err := x.validateExtras(ctx); err != nil { + return err } if v := x.Value; v != nil { return v.Validate(ctx) } - return foundUnresolvedRef(x.Ref) + return newUnresolvedRef(x.Ref, x.Origin) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/refs_test.tmpl b/vendor/github.com/getkin/kin-openapi/openapi3/refs_test.tmpl index 634fccf6f..4aabdc4e9 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/refs_test.tmpl +++ b/vendor/github.com/getkin/kin-openapi/openapi3/refs_test.tmpl @@ -1,19 +1,21 @@ // Code generated by go generate; DO NOT EDIT. -package {{ .Package }} +package {{ .Package }}_test import ( - "context" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/getkin/kin-openapi/openapi3" ) {{ range $type := .Types }} func Test{{ $type.Name }}Ref_Extensions(t *testing.T) { data := []byte(`{"$ref":"#/components/schemas/Pet","something":"integer","x-order":1}`) + expectMarshalJson := []byte(`{"$ref":"#/components/schemas/Pet","x-order":1}`) - ref := {{ $type.Name }}Ref{} + ref := openapi3.{{ $type.Name }}Ref{} err := json.Unmarshal(data, &ref) assert.NoError(t, err) @@ -25,21 +27,27 @@ func Test{{ $type.Name }}Ref_Extensions(t *testing.T) { assert.Nil(t, ref.Extensions["something"]) // validation - err = ref.Validate(context.Background()) + err = ref.Validate(t.Context()) require.EqualError(t, err, "extra sibling fields: [something]") - err = ref.Validate(context.Background(), ProhibitExtensionsWithRef()) + err = ref.Validate(t.Context(), openapi3.ProhibitExtensionsWithRef()) require.EqualError(t, err, "extra sibling fields: [something x-order]") - err = ref.Validate(context.Background(), AllowExtraSiblingFields("something")) + err = ref.Validate(t.Context(), openapi3.AllowExtraSiblingFields("something")) assert.ErrorContains(t, err, "found unresolved ref") // expected since value not defined + // Verify round trip JSON + // Compare as string to make error message more readable if different + outJson, err := ref.MarshalJSON() + assert.NoError(t, err) + assert.Equal(t, string(outJson), string(expectMarshalJson), "MarshalJSON output is not the same as input data") + // non-extension not json lookable _, err = ref.JSONLookup("something") assert.Error(t, err) {{ if ne $type.Name "Header" }} t.Run("extentions in value", func(t *testing.T) { - ref.Value = &{{ $type.Name }}{Extensions: map[string]any{}} + ref.Value = &openapi3.{{ $type.Name }}{Extensions: map[string]any{}} ref.Value.Extensions["x-order"] = 2.0 // prefers the value next to the \$ref over the one in the \$ref. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go b/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go index 6c15ba051..e8c52a1cb 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go @@ -3,14 +3,14 @@ package openapi3 import ( "context" "encoding/json" - "errors" + "maps" ) // RequestBody is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#request-body-object type RequestBody struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"` @@ -86,9 +86,7 @@ func (requestBody RequestBody) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of RequestBody. func (requestBody RequestBody) MarshalYAML() (any, error) { m := make(map[string]any, 3+len(requestBody.Extensions)) - for k, v := range requestBody.Extensions { - m[k] = v - } + maps.Copy(m, requestBody.Extensions) if x := requestBody.Description; x != "" { m["description"] = requestBody.Description } @@ -109,7 +107,6 @@ func (requestBody *RequestBody) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "description") delete(x.Extensions, "required") delete(x.Extensions, "content") @@ -125,7 +122,7 @@ func (requestBody *RequestBody) Validate(ctx context.Context, opts ...Validation ctx = WithValidationOptions(ctx, opts...) if requestBody.Content == nil { - return errors.New("content of the request body is required") + return newRequestBodyContentRequired(requestBody.Origin) } if vo := getValidationOptions(ctx); !vo.examplesValidationDisabled { @@ -136,11 +133,11 @@ func (requestBody *RequestBody) Validate(ctx context.Context, opts ...Validation return err } - return validateExtensions(ctx, requestBody.Extensions) + return validateExtensions(ctx, requestBody.Extensions, requestBody.Origin) } // UnmarshalJSON sets RequestBodies to a copy of data. func (requestBodies *RequestBodies) UnmarshalJSON(data []byte) (err error) { - *requestBodies, _, err = unmarshalStringMapP[RequestBodyRef](data) + *requestBodies, err = unmarshalStringMapP[RequestBodyRef](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/response.go b/vendor/github.com/getkin/kin-openapi/openapi3/response.go index b87324a28..87a52fe24 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/response.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/response.go @@ -3,8 +3,7 @@ package openapi3 import ( "context" "encoding/json" - "errors" - "sort" + "maps" "strconv" ) @@ -15,14 +14,13 @@ type Responses struct { Origin *Origin `json:"-" yaml:"-"` m map[string]*ResponseRef + + explicitlyNull bool } // NewResponses builds a responses object with response objects in insertion order. -// Given no arguments, NewResponses returns a valid responses object containing a default match-all reponse. +// Given no arguments, NewResponses returns an empty responses object. func NewResponses(opts ...NewResponsesOption) *Responses { - if len(opts) == 0 { - return NewResponses(WithName("default", NewResponse().WithDescription(""))) - } responses := NewResponsesWithCapacity(len(opts)) for _, opt := range opts { opt(responses) @@ -30,6 +28,10 @@ func NewResponses(opts ...NewResponsesOption) *Responses { return responses } +func (responses *Responses) isExplicitlyNull() bool { + return responses != nil && responses.explicitlyNull && responses.m == nil && len(responses.Extensions) == 0 +} + // NewResponsesOption describes options to NewResponses func type NewResponsesOption func(*Responses) @@ -79,31 +81,31 @@ func (responses *Responses) Status(status int) *ResponseRef { // Validate returns an error if Responses does not comply with the OpenAPI spec. func (responses *Responses) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) if responses.Len() == 0 { - return errors.New("the responses object MUST contain at least one response code") + if err := me.emit(newResponsesNonEmptyRequired(responses.Origin)); err != nil { + return err + } + // Fall through so validateExtensions still runs and any extension + // errors aggregate with the empty-responses finding under multi mode. } - keys := make([]string, 0, responses.Len()) - for key := range responses.Map() { - keys = append(keys, key) - } - sort.Strings(keys) - for _, key := range keys { + for _, key := range responses.Keys() { v := responses.Value(key) - if err := v.Validate(ctx); err != nil { + if err := me.emit(v.Validate(ctx)); err != nil { return err } } - return validateExtensions(ctx, responses.Extensions) + return me.finalize(validateExtensions(ctx, responses.Extensions, responses.Origin)) } // Response is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#response-object type Response struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Description *string `json:"description,omitempty" yaml:"description,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` @@ -147,9 +149,7 @@ func (response Response) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Response. func (response Response) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(response.Extensions)) - for k, v := range response.Extensions { - m[k] = v - } + maps.Copy(m, response.Extensions) if x := response.Description; x != nil { m["description"] = x } @@ -173,7 +173,6 @@ func (response *Response) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "description") delete(x.Extensions, "headers") delete(x.Extensions, "content") @@ -190,7 +189,7 @@ func (response *Response) Validate(ctx context.Context, opts ...ValidationOption ctx = WithValidationOptions(ctx, opts...) if response.Description == nil { - return errors.New("a short description of the response is required") + return newResponseDescriptionRequired(response.Origin) } if vo := getValidationOptions(ctx); !vo.examplesValidationDisabled { vo.examplesValidationAsReq, vo.examplesValidationAsRes = false, true @@ -202,35 +201,25 @@ func (response *Response) Validate(ctx context.Context, opts ...ValidationOption } } - headers := make([]string, 0, len(response.Headers)) - for name := range response.Headers { - headers = append(headers, name) - } - sort.Strings(headers) - for _, name := range headers { + for _, name := range componentNames(response.Headers) { header := response.Headers[name] if err := header.Validate(ctx); err != nil { return err } } - links := make([]string, 0, len(response.Links)) - for name := range response.Links { - links = append(links, name) - } - sort.Strings(links) - for _, name := range links { + for _, name := range componentNames(response.Links) { link := response.Links[name] if err := link.Validate(ctx); err != nil { return err } } - return validateExtensions(ctx, response.Extensions) + return validateExtensions(ctx, response.Extensions, response.Origin) } // UnmarshalJSON sets ResponseBodies to a copy of data. func (responseBodies *ResponseBodies) UnmarshalJSON(data []byte) (err error) { - *responseBodies, _, err = unmarshalStringMapP[ResponseRef](data) + *responseBodies, err = unmarshalStringMapP[ResponseRef](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/schema.go b/vendor/github.com/getkin/kin-openapi/openapi3/schema.go index 75bcf1268..2934971d2 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/schema.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/schema.go @@ -6,17 +6,17 @@ import ( "encoding/json" "errors" "fmt" + "maps" "math" "math/big" "reflect" - "sort" + "slices" "strconv" "strings" "sync" "unicode/utf16" "github.com/go-openapi/jsonpointer" - "github.com/mohae/deepcopy" ) const ( @@ -79,9 +79,10 @@ func (s SchemaRefs) JSONLookup(token string) (any, error) { // Schema is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schema-object +// and https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object type Schema struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` OneOf SchemaRefs `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` AnyOf SchemaRefs `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` @@ -99,8 +100,8 @@ type Schema struct { // Array-related, here for struct compactness UniqueItems bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` // Number-related, here for struct compactness - ExclusiveMin bool `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` - ExclusiveMax bool `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"` + ExclusiveMin ExclusiveBound `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` // Number for v3.1+ otherwise boolean + ExclusiveMax ExclusiveBound `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"` // Number for v3.1+ otherwise boolean // Properties Nullable bool `json:"nullable,omitempty" yaml:"nullable,omitempty"` ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` @@ -131,14 +132,82 @@ type Schema struct { MaxProps *uint64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` AdditionalProperties AdditionalProperties `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"` Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` -} + Const any `json:"const,omitempty" yaml:"const,omitempty"` // OpenAPI >=3.1 + Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"` // OpenAPI >=3.1 + PrefixItems SchemaRefs `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"` // OpenAPI >=3.1 + Contains *SchemaRef `json:"contains,omitempty" yaml:"contains,omitempty"` // OpenAPI >=3.1 + MinContains *uint64 `json:"minContains,omitempty" yaml:"minContains,omitempty"` // OpenAPI >=3.1 + MaxContains *uint64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"` // OpenAPI >=3.1 + PatternProperties Schemas `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` // OpenAPI >=3.1 + DependentSchemas Schemas `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"` // OpenAPI >=3.1 + PropertyNames *SchemaRef `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` // OpenAPI >=3.1 + UnevaluatedItems BoolSchema `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"` // OpenAPI >=3.1 + UnevaluatedProperties BoolSchema `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"` // OpenAPI >=3.1 + + If *SchemaRef `json:"if,omitempty" yaml:"if,omitempty"` // OpenAPI >=3.1 + Then *SchemaRef `json:"then,omitempty" yaml:"then,omitempty"` // OpenAPI >=3.1 + Else *SchemaRef `json:"else,omitempty" yaml:"else,omitempty"` // OpenAPI >=3.1 + + DependentRequired map[string][]string `json:"dependentRequired,omitempty" yaml:"dependentRequired,omitempty"` // OpenAPI >=3.1 + + Defs Schemas `json:"$defs,omitempty" yaml:"$defs,omitempty"` // OpenAPI >=3.1 + SchemaDialect string `json:"$schema,omitempty" yaml:"$schema,omitempty"` // OpenAPI >=3.1 + Comment string `json:"$comment,omitempty" yaml:"$comment,omitempty"` // OpenAPI >=3.1 + + SchemaID string `json:"$id,omitempty" yaml:"$id,omitempty"` // OpenAPI >=3.1 + Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"` // OpenAPI >=3.1 + DynamicRef string `json:"$dynamicRef,omitempty" yaml:"$dynamicRef,omitempty"` // OpenAPI >=3.1 + DynamicAnchor string `json:"$dynamicAnchor,omitempty" yaml:"$dynamicAnchor,omitempty"` // OpenAPI >=3.1 + + ContentMediaType string `json:"contentMediaType,omitempty" yaml:"contentMediaType,omitempty"` // OpenAPI >=3.1 + ContentEncoding string `json:"contentEncoding,omitempty" yaml:"contentEncoding,omitempty"` // OpenAPI >=3.1 + ContentSchema *SchemaRef `json:"contentSchema,omitempty" yaml:"contentSchema,omitempty"` // OpenAPI >=3.1 +} + +// Types represents the type(s) of a schema. +// +// In OpenAPI 3.0, this is typically a single type (e.g., "string"). +// In OpenAPI 3.1, it can be an array of types (e.g., ["string", "null"]). +// +// Serialization behavior: +// - Single type: serializes as a string (e.g., "string") +// - Multiple types: serializes as an array (e.g., ["string", "null"]) +// - Accepts both string and array formats when unmarshaling +// +// Example OpenAPI 3.0 (single type): +// +// schema := &Schema{Type: &Types{"string"}} +// // JSON: {"type": "string"} +// +// Example OpenAPI 3.1 (type array): +// +// schema := &Schema{Type: &Types{"string", "null"}} +// // JSON: {"type": ["string", "null"]} type Types []string +// Is returns true if the schema has exactly one type and it matches the given type. +// This is useful for OpenAPI 3.0 style single-type checks. +// +// Example: +// +// types := &Types{"string"} +// types.Is("string") // true +// types.Is("number") // false +// +// types = &Types{"string", "null"} +// types.Is("string") // false (multiple types) func (types *Types) Is(typ string) bool { return types != nil && len(*types) == 1 && (*types)[0] == typ } +// Slice returns the types as a string slice. +// Returns nil if types is nil. +// +// Example: +// +// types := &Types{"string", "null"} +// slice := types.Slice() // []string{"string", "null"} func (types *Types) Slice() []string { if types == nil { return nil @@ -146,19 +215,33 @@ func (types *Types) Slice() []string { return *types } +// Includes returns true if the given type is included in the type array. +// Returns false if types is nil. +// +// Example: +// +// types := &Types{"string", "null"} +// types.Includes("string") // true +// types.Includes("null") // true +// types.Includes("number") // false func (pTypes *Types) Includes(typ string) bool { if pTypes == nil { return false } - types := *pTypes - for _, candidate := range types { - if candidate == typ { - return true - } - } - return false + return slices.Contains(*pTypes, typ) } +// Permits returns true if the given type is permitted. +// Returns true if types is nil (any type allowed), otherwise checks if the type is included. +// +// Example: +// +// var nilTypes *Types +// nilTypes.Permits("anything") // true (nil permits everything) +// +// types := &Types{"string"} +// types.Permits("string") // true +// types.Permits("number") // false func (types *Types) Permits(typ string) bool { if types == nil { return true @@ -166,6 +249,64 @@ func (types *Types) Permits(typ string) bool { return types.Includes(typ) } +// IncludesNull returns true if the type array includes "null". +// This is useful for OpenAPI 3.1 where null is a first-class type. +// +// Example: +// +// types := &Types{"string", "null"} +// types.IncludesNull() // true +// +// types = &Types{"string"} +// types.IncludesNull() // false +func (types *Types) IncludesNull() bool { + return types.Includes(TypeNull) +} + +// IsMultiple returns true if multiple types are specified. +// This is an OpenAPI 3.1 feature that enables type arrays. +// +// Example: +// +// types := &Types{"string"} +// types.IsMultiple() // false +// +// types = &Types{"string", "null"} +// types.IsMultiple() // true +func (types *Types) IsMultiple() bool { + return types != nil && len(*types) > 1 +} + +// IsSingle returns true if exactly one type is specified. +// +// Example: +// +// types := &Types{"string"} +// types.IsSingle() // true +// +// types = &Types{"string", "null"} +// types.IsSingle() // false +func (types *Types) IsSingle() bool { + return types != nil && len(*types) == 1 +} + +// IsEmpty returns true if no types are specified (nil or empty array). +// When a schema has no type specified, it permits any type. +// +// Example: +// +// var nilTypes *Types +// nilTypes.IsEmpty() // true +// +// types := &Types{} +// types.IsEmpty() // true +// +// types = &Types{"string"} +// types.IsEmpty() // false +func (types *Types) IsEmpty() bool { + return types == nil || len(*types) == 0 +} + func (pTypes *Types) MarshalJSON() ([]byte, error) { x, err := pTypes.MarshalYAML() if err != nil { @@ -202,36 +343,41 @@ func (types *Types) UnmarshalJSON(data []byte) error { return nil } -type AdditionalProperties struct { +// BoolSchema represents a JSON Schema keyword that can be either a boolean or a schema object. +// Used for additionalProperties, unevaluatedProperties, and unevaluatedItems. +type BoolSchema struct { Has *bool Schema *SchemaRef } -// MarshalYAML returns the YAML encoding of AdditionalProperties. -func (addProps AdditionalProperties) MarshalYAML() (any, error) { - if x := addProps.Has; x != nil { +// AdditionalProperties is a type alias for BoolSchema, kept for backward compatibility. +type AdditionalProperties = BoolSchema + +// MarshalYAML returns the YAML encoding of BoolSchema. +func (bs BoolSchema) MarshalYAML() (any, error) { + if x := bs.Has; x != nil { if *x { return true, nil } return false, nil } - if x := addProps.Schema; x != nil { + if x := bs.Schema; x != nil { return x.MarshalYAML() } return nil, nil } -// MarshalJSON returns the JSON encoding of AdditionalProperties. -func (addProps AdditionalProperties) MarshalJSON() ([]byte, error) { - x, err := addProps.MarshalYAML() +// MarshalJSON returns the JSON encoding of BoolSchema. +func (bs BoolSchema) MarshalJSON() ([]byte, error) { + x, err := bs.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } -// UnmarshalJSON sets AdditionalProperties to a copy of data. -func (addProps *AdditionalProperties) UnmarshalJSON(data []byte) error { +// UnmarshalJSON sets BoolSchema to a copy of data. +func (bs *BoolSchema) UnmarshalJSON(data []byte) error { var x any if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) @@ -239,19 +385,79 @@ func (addProps *AdditionalProperties) UnmarshalJSON(data []byte) error { switch y := x.(type) { case nil: case bool: - addProps.Has = &y + bs.Has = &y case map[string]any: if len(y) == 0 { - addProps.Schema = &SchemaRef{Value: &Schema{}} + bs.Schema = &SchemaRef{Value: &Schema{}} } else { buf := new(bytes.Buffer) - json.NewEncoder(buf).Encode(y) - if err := json.NewDecoder(buf).Decode(&addProps.Schema); err != nil { + _ = json.NewEncoder(buf).Encode(y) + if err := json.NewDecoder(buf).Decode(&bs.Schema); err != nil { return err } } default: - return errors.New("cannot unmarshal additionalProperties: value must be either a schema object or a boolean") + return errors.New("cannot unmarshal: value must be either a schema object or a boolean") + } + return nil +} + +// ExclusiveBound represents exclusiveMinimum/exclusiveMaximum which changed type between OpenAPI versions. +// In OpenAPI 3.0 (JSON Schema draft-04): boolean that modifies minimum/maximum +// In OpenAPI 3.1 (JSON Schema 2020-12): number representing the actual exclusive bound +type ExclusiveBound struct { + Bool *bool // For OpenAPI 3.0 style (modifier for min/max) + Value *float64 // For OpenAPI 3.1 style (actual bound value) +} + +// IsSet returns true if either Bool or Value is set. +func (eb ExclusiveBound) IsSet() bool { + return eb.Bool != nil || eb.Value != nil +} + +// IsTrue returns true if the bound is set as a boolean true (OpenAPI 3.0 style). +func (eb ExclusiveBound) IsTrue() bool { + return eb.Bool != nil && *eb.Bool +} + +// MarshalYAML returns the YAML encoding of ExclusiveBound. +func (eb ExclusiveBound) MarshalYAML() (any, error) { + if eb.Value != nil { + return *eb.Value, nil + } + if eb.Bool != nil { + return *eb.Bool, nil + } + return nil, nil +} + +// MarshalJSON returns the JSON encoding of ExclusiveBound. +func (eb ExclusiveBound) MarshalJSON() ([]byte, error) { + x, err := eb.MarshalYAML() + if err != nil { + return nil, err + } + if x == nil { + return nil, nil + } + return json.Marshal(x) +} + +// UnmarshalJSON sets ExclusiveBound to a copy of data. +func (eb *ExclusiveBound) UnmarshalJSON(data []byte) error { + var x any + if err := json.Unmarshal(data, &x); err != nil { + return unmarshalError(err) + } + switch y := x.(type) { + case nil: + // nothing to do + case bool: + eb.Bool = &y + case float64: + eb.Value = &y + default: + return errors.New("cannot unmarshal exclusiveMinimum/exclusiveMaximum: value must be either a number or a boolean") } return nil } @@ -274,10 +480,8 @@ func (schema Schema) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Schema. func (schema Schema) MarshalYAML() (any, error) { - m := make(map[string]any, 36+len(schema.Extensions)) - for k, v := range schema.Extensions { - m[k] = v - } + m := make(map[string]any, 61+len(schema.Extensions)) + maps.Copy(m, schema.Extensions) if x := schema.OneOf; len(x) != 0 { m["oneOf"] = x @@ -321,10 +525,10 @@ func (schema Schema) MarshalYAML() (any, error) { m["uniqueItems"] = x } // Number-related - if x := schema.ExclusiveMin; x { + if x := schema.ExclusiveMin; x.IsSet() { m["exclusiveMinimum"] = x } - if x := schema.ExclusiveMax; x { + if x := schema.ExclusiveMax; x.IsSet() { m["exclusiveMaximum"] = x } // Properties @@ -400,6 +604,83 @@ func (schema Schema) MarshalYAML() (any, error) { m["discriminator"] = x } + // OpenAPI 3.1 / JSON Schema 2020-12 fields + if x := schema.Const; x != nil { + m["const"] = x + } + if x := schema.Examples; len(x) != 0 { + m["examples"] = x + } + if x := schema.PrefixItems; len(x) != 0 { + m["prefixItems"] = x + } + if x := schema.Contains; x != nil { + m["contains"] = x + } + if x := schema.MinContains; x != nil { + m["minContains"] = x + } + if x := schema.MaxContains; x != nil { + m["maxContains"] = x + } + if x := schema.PatternProperties; len(x) != 0 { + m["patternProperties"] = x + } + if x := schema.DependentSchemas; len(x) != 0 { + m["dependentSchemas"] = x + } + if x := schema.PropertyNames; x != nil { + m["propertyNames"] = x + } + if x := schema.UnevaluatedItems; x.Has != nil || x.Schema != nil { + m["unevaluatedItems"] = &x + } + if x := schema.UnevaluatedProperties; x.Has != nil || x.Schema != nil { + m["unevaluatedProperties"] = &x + } + if x := schema.If; x != nil { + m["if"] = x + } + if x := schema.Then; x != nil { + m["then"] = x + } + if x := schema.Else; x != nil { + m["else"] = x + } + if x := schema.DependentRequired; len(x) != 0 { + m["dependentRequired"] = x + } + if x := schema.Defs; len(x) != 0 { + m["$defs"] = x + } + if x := schema.SchemaDialect; x != "" { + m["$schema"] = x + } + if x := schema.Comment; x != "" { + m["$comment"] = x + } + if x := schema.SchemaID; x != "" { + m["$id"] = x + } + if x := schema.Anchor; x != "" { + m["$anchor"] = x + } + if x := schema.DynamicRef; x != "" { + m["$dynamicRef"] = x + } + if x := schema.DynamicAnchor; x != "" { + m["$dynamicAnchor"] = x + } + if x := schema.ContentMediaType; x != "" { + m["contentMediaType"] = x + } + if x := schema.ContentEncoding; x != "" { + m["contentEncoding"] = x + } + if x := schema.ContentSchema; x != nil { + m["contentSchema"] = x + } + return m, nil } @@ -412,7 +693,6 @@ func (schema *Schema) UnmarshalJSON(data []byte) error { } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "oneOf") delete(x.Extensions, "anyOf") delete(x.Extensions, "allOf") @@ -462,18 +742,38 @@ func (schema *Schema) UnmarshalJSON(data []byte) error { delete(x.Extensions, "additionalProperties") delete(x.Extensions, "discriminator") + // OpenAPI 3.1 / JSON Schema 2020-12 fields + delete(x.Extensions, "const") + delete(x.Extensions, "examples") + delete(x.Extensions, "prefixItems") + delete(x.Extensions, "contains") + delete(x.Extensions, "minContains") + delete(x.Extensions, "maxContains") + delete(x.Extensions, "patternProperties") + delete(x.Extensions, "dependentSchemas") + delete(x.Extensions, "propertyNames") + delete(x.Extensions, "unevaluatedItems") + delete(x.Extensions, "unevaluatedProperties") + delete(x.Extensions, "if") + delete(x.Extensions, "then") + delete(x.Extensions, "else") + delete(x.Extensions, "dependentRequired") + delete(x.Extensions, "$defs") + delete(x.Extensions, "$schema") + delete(x.Extensions, "$comment") + delete(x.Extensions, "$id") + delete(x.Extensions, "$anchor") + delete(x.Extensions, "$dynamicRef") + delete(x.Extensions, "$dynamicAnchor") + delete(x.Extensions, "contentMediaType") + delete(x.Extensions, "contentEncoding") + delete(x.Extensions, "contentSchema") + if len(x.Extensions) == 0 { x.Extensions = nil } *schema = Schema(x) - - if schema.Format == "date" { - // This is a fix for: https://github.com/getkin/kin-openapi/issues/697 - if eg, ok := schema.Example.(string); ok { - schema.Example = strings.TrimSuffix(eg, "T00:00:00Z") - } - } return nil } @@ -570,6 +870,104 @@ func (schema Schema) JSONLookup(token string) (any, error) { return schema.MaxProps, nil case "discriminator": return schema.Discriminator, nil + + // OpenAPI 3.1 / JSON Schema 2020-12 fields + case "const": + return schema.Const, nil + case "examples": + return schema.Examples, nil + case "prefixItems": + return schema.PrefixItems, nil + case "contains": + if schema.Contains != nil { + if schema.Contains.Ref != "" { + return &Ref{Ref: schema.Contains.Ref}, nil + } + return schema.Contains.Value, nil + } + case "minContains": + return schema.MinContains, nil + case "maxContains": + return schema.MaxContains, nil + case "patternProperties": + return schema.PatternProperties, nil + case "dependentSchemas": + return schema.DependentSchemas, nil + case "propertyNames": + if schema.PropertyNames != nil { + if schema.PropertyNames.Ref != "" { + return &Ref{Ref: schema.PropertyNames.Ref}, nil + } + return schema.PropertyNames.Value, nil + } + case "unevaluatedItems": + if ui := schema.UnevaluatedItems.Has; ui != nil { + return *ui, nil + } + if ui := schema.UnevaluatedItems.Schema; ui != nil { + if ui.Ref != "" { + return &Ref{Ref: ui.Ref}, nil + } + return ui.Value, nil + } + case "unevaluatedProperties": + if up := schema.UnevaluatedProperties.Has; up != nil { + return *up, nil + } + if up := schema.UnevaluatedProperties.Schema; up != nil { + if up.Ref != "" { + return &Ref{Ref: up.Ref}, nil + } + return up.Value, nil + } + case "if": + if schema.If != nil { + if schema.If.Ref != "" { + return &Ref{Ref: schema.If.Ref}, nil + } + return schema.If.Value, nil + } + case "then": + if schema.Then != nil { + if schema.Then.Ref != "" { + return &Ref{Ref: schema.Then.Ref}, nil + } + return schema.Then.Value, nil + } + case "else": + if schema.Else != nil { + if schema.Else.Ref != "" { + return &Ref{Ref: schema.Else.Ref}, nil + } + return schema.Else.Value, nil + } + case "dependentRequired": + return schema.DependentRequired, nil + case "$defs": + return schema.Defs, nil + case "$schema": + return schema.SchemaDialect, nil + case "$comment": + return schema.Comment, nil + case "$id": + return schema.SchemaID, nil + case "$anchor": + return schema.Anchor, nil + case "$dynamicRef": + return schema.DynamicRef, nil + case "$dynamicAnchor": + return schema.DynamicAnchor, nil + case "contentMediaType": + return schema.ContentMediaType, nil + case "contentEncoding": + return schema.ContentEncoding, nil + case "contentSchema": + if schema.ContentSchema != nil { + if schema.ContentSchema.Ref != "" { + return &Ref{Ref: schema.ContentSchema.Ref}, nil + } + return schema.ContentSchema.Value, nil + } } v, _, err := jsonpointer.GetForToken(schema.Extensions, token) @@ -699,13 +1097,27 @@ func (schema *Schema) WithMax(value float64) *Schema { return schema } +// WithExclusiveMin sets exclusiveMinimum as a boolean (OpenAPI 3.0 style). func (schema *Schema) WithExclusiveMin(value bool) *Schema { - schema.ExclusiveMin = value + schema.ExclusiveMin = ExclusiveBound{Bool: &value} return schema } +// WithExclusiveMax sets exclusiveMaximum as a boolean (OpenAPI 3.0 style). func (schema *Schema) WithExclusiveMax(value bool) *Schema { - schema.ExclusiveMax = value + schema.ExclusiveMax = ExclusiveBound{Bool: &value} + return schema +} + +// WithExclusiveMinValue sets exclusiveMinimum as a number (OpenAPI 3.1 style). +func (schema *Schema) WithExclusiveMinValue(value float64) *Schema { + schema.ExclusiveMin = ExclusiveBound{Value: &value} + return schema +} + +// WithExclusiveMaxValue sets exclusiveMaximum as a number (OpenAPI 3.1 style). +func (schema *Schema) WithExclusiveMaxValue(value float64) *Schema { + schema.ExclusiveMax = ExclusiveBound{Value: &value} return schema } @@ -799,12 +1211,10 @@ func (schema *Schema) WithProperty(name string, propertySchema *Schema) *Schema } func (schema *Schema) WithPropertyRef(name string, ref *SchemaRef) *Schema { - properties := schema.Properties - if properties == nil { - properties = make(Schemas) - schema.Properties = properties + if schema.Properties == nil { + schema.Properties = make(Schemas) } - properties[name] = ref + schema.Properties[name] = ref return schema } @@ -837,12 +1247,12 @@ func (schema *Schema) WithMaxProperties(i int64) *Schema { } func (schema *Schema) WithAnyAdditionalProperties() *Schema { - schema.AdditionalProperties = AdditionalProperties{Has: BoolPtr(true)} + schema.AdditionalProperties = AdditionalProperties{Has: Ptr(true)} return schema } func (schema *Schema) WithoutAdditionalProperties() *Schema { - schema.AdditionalProperties = AdditionalProperties{Has: BoolPtr(false)} + schema.AdditionalProperties = AdditionalProperties{Has: Ptr(false)} return schema } @@ -855,19 +1265,20 @@ func (schema *Schema) WithAdditionalProperties(v *Schema) *Schema { } func (schema *Schema) PermitsNull() bool { - return schema.Nullable || schema.Type.Includes("null") + return schema.Nullable || schema.Type.IncludesNull() } // IsEmpty tells whether schema is equivalent to the empty schema `{}`. func (schema *Schema) IsEmpty() bool { if schema.Type != nil || schema.Format != "" || len(schema.Enum) != 0 || - schema.UniqueItems || schema.ExclusiveMin || schema.ExclusiveMax || + schema.UniqueItems || schema.ExclusiveMin.IsSet() || schema.ExclusiveMax.IsSet() || schema.Nullable || schema.ReadOnly || schema.WriteOnly || schema.AllowEmptyValue || schema.Min != nil || schema.Max != nil || schema.MultipleOf != nil || schema.MinLength != 0 || schema.MaxLength != nil || schema.Pattern != "" || schema.MinItems != 0 || schema.MaxItems != nil || len(schema.Required) != 0 || - schema.MinProps != 0 || schema.MaxProps != nil { + schema.MinProps != 0 || schema.MaxProps != nil || + schema.Const != nil { return false } if n := schema.Not; n != nil && n.Value != nil && !n.Value.IsEmpty() { @@ -882,11 +1293,50 @@ func (schema *Schema) IsEmpty() bool { if items := schema.Items; items != nil && items.Value != nil && !items.Value.IsEmpty() { return false } + for _, s := range schema.PrefixItems { + if ss := s.Value; ss != nil && !ss.IsEmpty() { + return false + } + } + if c := schema.Contains; c != nil && c.Value != nil && !c.Value.IsEmpty() { + return false + } + if schema.MinContains != nil || schema.MaxContains != nil { + return false + } for _, s := range schema.Properties { if ss := s.Value; ss != nil && !ss.IsEmpty() { return false } } + for _, s := range schema.PatternProperties { + if ss := s.Value; ss != nil && !ss.IsEmpty() { + return false + } + } + for _, s := range schema.DependentSchemas { + if ss := s.Value; ss != nil && !ss.IsEmpty() { + return false + } + } + if pn := schema.PropertyNames; pn != nil && pn.Value != nil && !pn.Value.IsEmpty() { + return false + } + if ui := schema.UnevaluatedItems.Schema; ui != nil && ui.Value != nil && !ui.Value.IsEmpty() { + return false + } + if uih := schema.UnevaluatedItems.Has; uih != nil && !*uih { + return false + } + if up := schema.UnevaluatedProperties.Schema; up != nil && up.Value != nil && !up.Value.IsEmpty() { + return false + } + if uph := schema.UnevaluatedProperties.Has; uph != nil && !*uph { + return false + } + if len(schema.Examples) != 0 { + return false + } for _, s := range schema.OneOf { if ss := s.Value; ss != nil && !ss.IsEmpty() { return false @@ -902,71 +1352,252 @@ func (schema *Schema) IsEmpty() bool { return false } } + if f := schema.If; f != nil && f.Value != nil && !f.Value.IsEmpty() { + return false + } + if t := schema.Then; t != nil && t.Value != nil && !t.Value.IsEmpty() { + return false + } + if e := schema.Else; e != nil && e.Value != nil && !e.Value.IsEmpty() { + return false + } + if len(schema.DependentRequired) != 0 { + return false + } + if len(schema.Defs) != 0 { + return false + } + if schema.SchemaDialect != "" || schema.Comment != "" { + return false + } + if schema.SchemaID != "" || schema.Anchor != "" || schema.DynamicRef != "" || schema.DynamicAnchor != "" { + return false + } + if schema.ContentMediaType != "" || schema.ContentEncoding != "" { + return false + } + if cs := schema.ContentSchema; cs != nil && cs.Value != nil && !cs.Value.IsEmpty() { + return false + } return true } // Validate returns an error if Schema does not comply with the OpenAPI spec. func (schema *Schema) Validate(ctx context.Context, opts ...ValidationOption) error { + // Apply document-level validation options to the context ctx = WithValidationOptions(ctx, opts...) + + // Perform schema validation with the options in context _, err := schema.validate(ctx, []*Schema{}) return err } // returns the updated stack and an error if Schema does not comply with the OpenAPI spec. func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, error) { + if slices.Contains(stack, schema) { + return stack, nil + } + validationOpts := getValidationOptions(ctx) - for _, existing := range stack { - if existing == schema { - return stack, nil - } - } stack = append(stack, schema) if schema.ReadOnly && schema.WriteOnly { - return stack, errors.New("a property MUST NOT be marked as both readOnly and writeOnly being true") + return stack, newSchemaReadOnlyWriteOnlyExclusive(schema.Origin) + } + + // The elements of `required` MUST be unique (JSON Schema 2020-12 §6.5.3 + // for OAS 3.1, draft-04 for OAS 3.0). + if len(schema.Required) > 1 { + seen := make(map[string]struct{}, len(schema.Required)) + for _, name := range schema.Required { + if _, dup := seen[name]; dup { + return stack, &DuplicateRequiredFieldError{Field: name, Origin: schema.Origin} + } + seen[name] = struct{}{} + } + } + + // Reject fields that only exist in OAS 3.1 / JSON Schema 2020-12 when the + // document is OAS 3.0. Fields explicitly allowed via AllowExtraSiblingFields + // are skipped (opt-in escape hatch for 3.0 docs that reference external + // JSON Schema documents). Once 3.0 moves to its own schema validator this + // block becomes a no-op. + if !validationOpts.isOpenAPI31OrLater { + allowed := validationOpts.extraSiblingFieldsAllowed + reject := func(field string) error { + if _, ok := allowed[field]; ok { + return nil + } + return errFieldFor31Plus(field, schema.Origin) + } + if schema.Const != nil { + if err := reject("const"); err != nil { + return stack, err + } + } + if len(schema.Examples) != 0 { + if err := reject("examples"); err != nil { + return stack, err + } + } + if len(schema.PrefixItems) != 0 { + if err := reject("prefixItems"); err != nil { + return stack, err + } + } + if schema.Contains != nil { + if err := reject("contains"); err != nil { + return stack, err + } + } + if schema.MinContains != nil { + if err := reject("minContains"); err != nil { + return stack, err + } + } + if schema.MaxContains != nil { + if err := reject("maxContains"); err != nil { + return stack, err + } + } + if len(schema.PatternProperties) != 0 { + if err := reject("patternProperties"); err != nil { + return stack, err + } + } + if len(schema.DependentSchemas) != 0 { + if err := reject("dependentSchemas"); err != nil { + return stack, err + } + } + if schema.PropertyNames != nil { + if err := reject("propertyNames"); err != nil { + return stack, err + } + } + if schema.UnevaluatedItems.Has != nil || schema.UnevaluatedItems.Schema != nil { + if err := reject("unevaluatedItems"); err != nil { + return stack, err + } + } + if schema.UnevaluatedProperties.Has != nil || schema.UnevaluatedProperties.Schema != nil { + if err := reject("unevaluatedProperties"); err != nil { + return stack, err + } + } + if schema.If != nil { + if err := reject("if"); err != nil { + return stack, err + } + } + if schema.Then != nil { + if err := reject("then"); err != nil { + return stack, err + } + } + if schema.Else != nil { + if err := reject("else"); err != nil { + return stack, err + } + } + if len(schema.DependentRequired) != 0 { + if err := reject("dependentRequired"); err != nil { + return stack, err + } + } + if len(schema.Defs) != 0 { + if err := reject("$defs"); err != nil { + return stack, err + } + } + if schema.SchemaDialect != "" { + if err := reject("$schema"); err != nil { + return stack, err + } + } + if schema.Comment != "" { + if err := reject("$comment"); err != nil { + return stack, err + } + } + if schema.SchemaID != "" { + if err := reject("$id"); err != nil { + return stack, err + } + } + if schema.Anchor != "" { + if err := reject("$anchor"); err != nil { + return stack, err + } + } + if schema.DynamicRef != "" { + if err := reject("$dynamicRef"); err != nil { + return stack, err + } + } + if schema.DynamicAnchor != "" { + if err := reject("$dynamicAnchor"); err != nil { + return stack, err + } + } + if schema.ContentMediaType != "" { + if err := reject("contentMediaType"); err != nil { + return stack, err + } + } + if schema.ContentEncoding != "" { + if err := reject("contentEncoding"); err != nil { + return stack, err + } + } + if schema.ContentSchema != nil { + if err := reject("contentSchema"); err != nil { + return stack, err + } + } } for _, item := range schema.OneOf { v := item.Value if v == nil { - return stack, foundUnresolvedRef(item.Ref) + return stack, newUnresolvedRef(item.Ref, item.Origin) } var err error if stack, err = v.validate(ctx, stack); err != nil { - return stack, err + return stack, &SchemaCombinatorElementValidationError{Combinator: "oneOf", Cause: err} } } for _, item := range schema.AnyOf { v := item.Value if v == nil { - return stack, foundUnresolvedRef(item.Ref) + return stack, newUnresolvedRef(item.Ref, item.Origin) } var err error if stack, err = v.validate(ctx, stack); err != nil { - return stack, err + return stack, &SchemaCombinatorElementValidationError{Combinator: "anyOf", Cause: err} } } for _, item := range schema.AllOf { v := item.Value if v == nil { - return stack, foundUnresolvedRef(item.Ref) + return stack, newUnresolvedRef(item.Ref, item.Origin) } var err error if stack, err = v.validate(ctx, stack); err != nil { - return stack, err + return stack, &SchemaCombinatorElementValidationError{Combinator: "allOf", Cause: err} } } if ref := schema.Not; ref != nil { v := ref.Value if v == nil { - return stack, foundUnresolvedRef(ref.Ref) + return stack, newUnresolvedRef(ref.Ref, ref.Origin) } var err error @@ -975,6 +1606,37 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, } } + if ref := schema.If; ref != nil { + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + if ref := schema.Then; ref != nil { + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + if ref := schema.Else; ref != nil { + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + for _, schemaType := range schema.Type.Slice() { switch schemaType { case TypeBoolean: @@ -1025,19 +1687,26 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, } } case TypeArray: - if schema.Items == nil { - return stack, errors.New("when schema type is 'array', schema 'items' must be non-null") + if schema.Items == nil && !validationOpts.jsonSchema2020ValidationEnabled && len(schema.PrefixItems) == 0 { + return stack, newSchemaItemsRequired(schema.Origin) } case TypeObject: + case TypeNull: + if !validationOpts.jsonSchema2020ValidationEnabled { + return stack, newSchemaTypeError(schemaType, schema.Origin) + } default: - return stack, fmt.Errorf("unsupported 'type' value %q", schemaType) + return stack, newSchemaTypeError(schemaType, schema.Origin) } } if ref := schema.Items; ref != nil { + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } v := ref.Value if v == nil { - return stack, foundUnresolvedRef(ref.Ref) + return stack, newUnresolvedRef(ref.Ref, ref.Origin) } var err error @@ -1046,16 +1715,14 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, } } - properties := make([]string, 0, len(schema.Properties)) - for name := range schema.Properties { - properties = append(properties, name) - } - sort.Strings(properties) - for _, name := range properties { + for _, name := range componentNames(schema.Properties) { ref := schema.Properties[name] + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } v := ref.Value if v == nil { - return stack, foundUnresolvedRef(ref.Ref) + return stack, newUnresolvedRef(ref.Ref, ref.Origin) } var err error @@ -1065,12 +1732,152 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, } if schema.AdditionalProperties.Has != nil && schema.AdditionalProperties.Schema != nil { - return stack, errors.New("additionalProperties are set to both boolean and schema") + return stack, newSchemaAdditionalPropertiesBothForms(schema.Origin) } if ref := schema.AdditionalProperties.Schema; ref != nil { + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + + // OpenAPI 3.1 / JSON Schema 2020-12 sub-schemas + for _, ref := range schema.PrefixItems { + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } v := ref.Value if v == nil { - return stack, foundUnresolvedRef(ref.Ref) + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + if ref := schema.Contains; ref != nil { + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + for _, name := range componentNames(schema.PatternProperties) { + ref := schema.PatternProperties[name] + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + for _, name := range componentNames(schema.DependentSchemas) { + ref := schema.DependentSchemas[name] + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + for _, name := range componentNames(schema.Defs) { + ref := schema.Defs[name] + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + if ref := schema.PropertyNames; ref != nil { + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + if schema.UnevaluatedItems.Has != nil && schema.UnevaluatedItems.Schema != nil { + return stack, newSchemaUnevaluatedItemsBothForms(schema.Origin) + } + if ref := schema.UnevaluatedItems.Schema; ref != nil { + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + if schema.UnevaluatedProperties.Has != nil && schema.UnevaluatedProperties.Schema != nil { + return stack, newSchemaUnevaluatedPropertiesBothForms(schema.Origin) + } + if ref := schema.UnevaluatedProperties.Schema; ref != nil { + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) + } + + var err error + if stack, err = v.validate(ctx, stack); err != nil { + return stack, err + } + } + if ref := schema.ContentSchema; ref != nil { + if err := ref.validateExtras(ctx); err != nil { + return stack, err + } + v := ref.Value + if v == nil { + return stack, newUnresolvedRef(ref.Ref, ref.Origin) } var err error @@ -1081,23 +1888,23 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, if v := schema.ExternalDocs; v != nil { if err := v.Validate(ctx); err != nil { - return stack, fmt.Errorf("invalid external docs: %w", err) + return stack, &SectionValidationError{Section: "external docs", Cause: err} } } if v := schema.Default; v != nil && !validationOpts.schemaDefaultsValidationDisabled { - if err := schema.VisitJSON(v); err != nil { - return stack, fmt.Errorf("invalid default: %w", err) + if err := validateExampleValue(ctx, v, schema); err != nil { + return stack, newSchemaValueError("default", err, schema.Origin) } } if x := schema.Example; x != nil && !validationOpts.examplesValidationDisabled { if err := validateExampleValue(ctx, x, schema); err != nil { - return stack, fmt.Errorf("invalid example: %w", err) + return stack, newSchemaValueError("example", err, schema.Origin) } } - return stack, validateExtensions(ctx, schema.Extensions) + return stack, validateExtensions(ctx, schema.Extensions, schema.Origin) } func (schema *Schema) IsMatching(value any) bool { @@ -1130,8 +1937,14 @@ func (schema *Schema) IsMatchingJSONObject(value map[string]any) bool { return schema.visitJSON(settings, value) == nil } +// VisitJSON applies a Schema to the given data, considering opts. +// To validate data against an OpenAPIv3.1+ schema, be sure to pass the EnableJSONSchema2020() option. func (schema *Schema) VisitJSON(value any, opts ...SchemaValidationOption) error { settings := newSchemaValidationSettings(opts...) + + if settings.useJSONSchema2020 { + return schema.useJSONSchema2020(settings, value) + } return schema.visitJSON(settings, value) } @@ -1171,6 +1984,9 @@ func (schema *Schema) visitJSON(settings *schemaValidationSettings, value any) ( if err = schema.visitEnumOperation(settings, value); err != nil { return } + if err = schema.visitConstOperation(settings, value); err != nil { + return + } switch value := value.(type) { case nil: @@ -1265,7 +2081,40 @@ func (schema *Schema) visitEnumOperation(settings *schemaValidationSettings, val Value: value, Schema: schema, SchemaField: "enum", - Reason: fmt.Sprintf("value is not one of the allowed values %s", string(allowedValues)), + Reason: "value is not one of the allowed values " + string(allowedValues), + customizeMessageError: settings.customizeMessageError, + } + } + return +} + +func (schema *Schema) visitConstOperation(settings *schemaValidationSettings, value any) (err error) { + if schema.Const == nil { + return + } + var match bool + switch c := value.(type) { + case json.Number: + var f float64 + if f, err = strconv.ParseFloat(c.String(), 64); err != nil { + return err + } + match = reflect.DeepEqual(schema.Const, f) + case int64: + match = reflect.DeepEqual(schema.Const, float64(c)) + default: + match = reflect.DeepEqual(schema.Const, value) + } + if !match { + if settings.failfast { + return errSchema + } + constVal, _ := json.Marshal(schema.Const) + return &SchemaError{ + Value: value, + Schema: schema, + SchemaField: "const", + Reason: "value must be " + string(constVal), customizeMessageError: settings.customizeMessageError, } } @@ -1276,7 +2125,7 @@ func (schema *Schema) visitNotOperation(settings *schemaValidationSettings, valu if ref := schema.Not; ref != nil { v := ref.Value if v == nil { - return foundUnresolvedRef(ref.Ref) + return newUnresolvedRef(ref.Ref, ref.Origin) } if err := v.visitJSON(settings, value); err == nil { if settings.failfast { @@ -1295,41 +2144,54 @@ func (schema *Schema) visitNotOperation(settings *schemaValidationSettings, valu // If the XOF operations pass successfully, abort further run of validation, as they will already be satisfied (unless the schema // itself is badly specified +// resolveDiscriminatorRef resolves the discriminator reference for oneOf/anyOf validation. +// Returns the discriminator ref string and any error encountered during resolution. +func (schema *Schema) resolveDiscriminatorRef(value any) (string, error) { + if schema.Discriminator == nil { + return "", nil + } + pn := schema.Discriminator.PropertyName + valuemap, okcheck := value.(map[string]any) + if !okcheck { + return "", nil + } + discriminatorVal, okcheck := valuemap[pn] + if !okcheck { + return "", &SchemaError{ + Schema: schema, + SchemaField: "discriminator", + Reason: fmt.Sprintf("input does not contain the discriminator property %q", pn), + } + } + + discriminatorValString, okcheck := discriminatorVal.(string) + if !okcheck { + return "", &SchemaError{ + Value: discriminatorVal, + Schema: schema, + SchemaField: "discriminator", + Reason: fmt.Sprintf("value of discriminator property %q is not a string", pn), + } + } + + if discriminatorRef, okcheck := schema.Discriminator.Mapping[discriminatorValString]; len(schema.Discriminator.Mapping) > 0 && !okcheck { + return "", &SchemaError{ + Value: discriminatorVal, + Schema: schema, + SchemaField: "discriminator", + Reason: fmt.Sprintf("discriminator property %q has invalid value", pn), + } + } else { + return discriminatorRef.Ref, nil + } +} + func (schema *Schema) visitXOFOperations(settings *schemaValidationSettings, value any) (err error, run bool) { var visitedOneOf, visitedAnyOf, visitedAllOf bool if v := schema.OneOf; len(v) > 0 { - var discriminatorRef string - if schema.Discriminator != nil { - pn := schema.Discriminator.PropertyName - if valuemap, okcheck := value.(map[string]any); okcheck { - discriminatorVal, okcheck := valuemap[pn] - if !okcheck { - return &SchemaError{ - Schema: schema, - SchemaField: "discriminator", - Reason: fmt.Sprintf("input does not contain the discriminator property %q", pn), - }, false - } - - discriminatorValString, okcheck := discriminatorVal.(string) - if !okcheck { - return &SchemaError{ - Value: discriminatorVal, - Schema: schema, - SchemaField: "discriminator", - Reason: fmt.Sprintf("value of discriminator property %q is not a string", pn), - }, false - } - - if discriminatorRef, okcheck = schema.Discriminator.Mapping[discriminatorValString]; len(schema.Discriminator.Mapping) > 0 && !okcheck { - return &SchemaError{ - Value: discriminatorVal, - Schema: schema, - SchemaField: "discriminator", - Reason: fmt.Sprintf("discriminator property %q has invalid value", pn), - }, false - } - } + discriminatorRef, err := schema.resolveDiscriminatorRef(value) + if err != nil { + return err, false } var ( @@ -1341,7 +2203,7 @@ func (schema *Schema) visitXOFOperations(settings *schemaValidationSettings, val for idx, item := range v { v := item.Value if v == nil { - return foundUnresolvedRef(item.Ref), false + return newUnresolvedRef(item.Ref, item.Origin), false } if discriminatorRef != "" && discriminatorRef != item.Ref { @@ -1350,7 +2212,7 @@ func (schema *Schema) visitXOFOperations(settings *schemaValidationSettings, val // make a deep copy to protect origin value from being injected default value that defined in mismatched oneOf schema if settings.asreq || settings.asrep { - tempValue = deepcopy.Copy(value) + tempValue = deepCopyJSONValue(value) } if err := v.visitJSON(settings, tempValue); err != nil { @@ -1391,6 +2253,11 @@ func (schema *Schema) visitXOFOperations(settings *schemaValidationSettings, val } if v := schema.AnyOf; len(v) > 0 { + discriminatorRef, err := schema.resolveDiscriminatorRef(value) + if err != nil { + return err, false + } + var ( ok = false matchedAnyOfIdx = 0 @@ -1399,11 +2266,16 @@ func (schema *Schema) visitXOFOperations(settings *schemaValidationSettings, val for idx, item := range v { v := item.Value if v == nil { - return foundUnresolvedRef(item.Ref), false + return newUnresolvedRef(item.Ref, item.Origin), false } + + if discriminatorRef != "" && discriminatorRef != item.Ref { + continue + } + // make a deep copy to protect origin value from being injected default value that defined in mismatched anyOf schema if settings.asreq || settings.asrep { - tempValue = deepcopy.Copy(value) + tempValue = deepCopyJSONValue(value) } if err := v.visitJSON(settings, tempValue); err == nil { ok = true @@ -1428,26 +2300,30 @@ func (schema *Schema) visitXOFOperations(settings *schemaValidationSettings, val visitedAnyOf = true } + validationErrors := multiErrorForAllOf{} for _, item := range schema.AllOf { v := item.Value if v == nil { - return foundUnresolvedRef(item.Ref), false + return newUnresolvedRef(item.Ref, item.Origin), false } if err := v.visitJSON(settings, value); err != nil { if settings.failfast { return errSchema, false } - return &SchemaError{ - Value: value, - Schema: schema, - SchemaField: "allOf", - Reason: `doesn't match all schemas from "allOf"`, - Origin: err, - customizeMessageError: settings.customizeMessageError, - }, false + validationErrors = append(validationErrors, err) } visitedAllOf = true } + if len(validationErrors) > 0 { + return &SchemaError{ + Value: value, + Schema: schema, + SchemaField: "allOf", + Reason: `doesn't match all schemas from "allOf"`, + Origin: fmt.Errorf("doesn't match schema due to: %w", validationErrors), + customizeMessageError: settings.customizeMessageError, + }, false + } run = !((visitedOneOf || visitedAnyOf || visitedAllOf) && value == nil) return @@ -1522,7 +2398,12 @@ func (schema *Schema) visitJSONNumber(settings *schemaValidationSettings, value format := schema.Format if format != "" { if requireInteger { - if f, ok := SchemaIntegerFormats[format]; ok { + // Check per-validation validators first, then fall back to global + f, ok := settings.integerFormats[format] + if !ok { + f, ok = SchemaIntegerFormats[format] + } + if ok { if err := f.Validate(int64(value)); err != nil { var reason string schemaErr := &SchemaError{} @@ -1536,7 +2417,12 @@ func (schema *Schema) visitJSONNumber(settings *schemaValidationSettings, value } } } else { - if f, ok := SchemaNumberFormats[format]; ok { + // Check per-validation validators first, then fall back to global + f, ok := settings.numberFormats[format] + if !ok { + f, ok = SchemaNumberFormats[format] + } + if ok { if err := f.Validate(value); err != nil { var reason string schemaErr := &SchemaError{} @@ -1568,39 +2454,73 @@ func (schema *Schema) visitJSONNumber(settings *schemaValidationSettings, value } // "exclusiveMinimum" - if v := schema.ExclusiveMin; v && !(*schema.Min < value) { - if settings.failfast { - return errSchema - } - err := &SchemaError{ - Value: value, - Schema: schema, - SchemaField: "exclusiveMinimum", - Reason: fmt.Sprintf("number must be more than %g", *schema.Min), - customizeMessageError: settings.customizeMessageError, + // OpenAPI 3.0: boolean modifier for minimum + // OpenAPI 3.1: number representing the actual exclusive bound + if eb := schema.ExclusiveMin; eb.IsSet() { + var exclusiveMinBound float64 + var valid bool + if eb.Value != nil { + // OpenAPI 3.1 style: exclusiveMinimum is the bound itself + exclusiveMinBound = *eb.Value + valid = value > exclusiveMinBound + } else if eb.Bool != nil && *eb.Bool && schema.Min != nil { + // OpenAPI 3.0 style: exclusiveMinimum modifies minimum + exclusiveMinBound = *schema.Min + valid = value > exclusiveMinBound + } else { + valid = true } - if !settings.multiError { - return err + if !valid { + if settings.failfast { + return errSchema + } + err := &SchemaError{ + Value: value, + Schema: schema, + SchemaField: "exclusiveMinimum", + Reason: fmt.Sprintf("number must be more than %g", exclusiveMinBound), + customizeMessageError: settings.customizeMessageError, + } + if !settings.multiError { + return err + } + me = append(me, err) } - me = append(me, err) } // "exclusiveMaximum" - if v := schema.ExclusiveMax; v && !(*schema.Max > value) { - if settings.failfast { - return errSchema - } - err := &SchemaError{ - Value: value, - Schema: schema, - SchemaField: "exclusiveMaximum", - Reason: fmt.Sprintf("number must be less than %g", *schema.Max), - customizeMessageError: settings.customizeMessageError, + // OpenAPI 3.0: boolean modifier for maximum + // OpenAPI 3.1: number representing the actual exclusive bound + if eb := schema.ExclusiveMax; eb.IsSet() { + var exclusiveMaxBound float64 + var valid bool + if eb.Value != nil { + // OpenAPI 3.1 style: exclusiveMaximum is the bound itself + exclusiveMaxBound = *eb.Value + valid = value < exclusiveMaxBound + } else if eb.Bool != nil && *eb.Bool && schema.Max != nil { + // OpenAPI 3.0 style: exclusiveMaximum modifies maximum + exclusiveMaxBound = *schema.Max + valid = value < exclusiveMaxBound + } else { + valid = true } - if !settings.multiError { - return err + if !valid { + if settings.failfast { + return errSchema + } + err := &SchemaError{ + Value: value, + Schema: schema, + SchemaField: "exclusiveMaximum", + Reason: fmt.Sprintf("number must be less than %g", exclusiveMaxBound), + customizeMessageError: settings.customizeMessageError, + } + if !settings.multiError { + return err + } + me = append(me, err) } - me = append(me, err) } // "minimum" @@ -1643,7 +2563,10 @@ func (schema *Schema) visitJSONNumber(settings *schemaValidationSettings, value if v := schema.MultipleOf; v != nil { // "A numeric instance is valid only if division by this keyword's // value results in an integer." - if bigFloat := big.NewFloat(value / *v); !bigFloat.IsInt() { + numRat, denRat := &big.Rat{}, &big.Rat{} + numRat.SetString(fmt.Sprintf("%.10f", value)) + denRat.SetString(fmt.Sprintf("%.10f", *v)) + if !(&big.Rat{}).Quo(numRat, denRat).IsInt() { if settings.failfast { return errSchema } @@ -1759,7 +2682,12 @@ func (schema *Schema) visitJSONString(settings *schemaValidationSettings, value var formatStrErr string var formatErr error if format := schema.Format; format != "" { - if f, ok := SchemaStringFormats[format]; ok { + // Check per-validation validators first, then fall back to global + f, ok := settings.stringFormats[format] + if !ok { + f, ok = SchemaStringFormats[format] + } + if ok { if err := f.Validate(value); err != nil { var reason string schemaErr := &SchemaError{} @@ -1871,7 +2799,7 @@ func (schema *Schema) visitJSONArray(settings *schemaValidationSettings, value [ if itemSchemaRef := schema.Items; itemSchemaRef != nil { itemSchema := itemSchemaRef.Value if itemSchema == nil { - return foundUnresolvedRef(itemSchemaRef.Ref) + return newUnresolvedRef(itemSchemaRef.Ref, itemSchemaRef.Origin) } for i, item := range value { if err := itemSchema.visitJSON(settings, item); err != nil { @@ -1908,12 +2836,7 @@ func (schema *Schema) visitJSONObject(settings *schemaValidationSettings, value var me MultiError if settings.asreq || settings.asrep { - properties := make([]string, 0, len(schema.Properties)) - for propName := range schema.Properties { - properties = append(properties, propName) - } - sort.Strings(properties) - for _, propName := range properties { + for _, propName := range componentNames(schema.Properties) { propSchema := schema.Properties[propName] reqRO := settings.asreq && propSchema.Value.ReadOnly && !settings.readOnlyValidationDisabled repWO := settings.asrep && propSchema.Value.WriteOnly && !settings.writeOnlyValidationDisabled @@ -1980,19 +2903,14 @@ func (schema *Schema) visitJSONObject(settings *schemaValidationSettings, value if ref := schema.AdditionalProperties.Schema; ref != nil { additionalProperties = ref.Value } - keys := make([]string, 0, len(value)) - for k := range value { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { + for _, k := range componentNames(value) { v := value[k] if properties != nil { propertyRef := properties[k] if propertyRef != nil { p := propertyRef.Value if p == nil { - return foundUnresolvedRef(propertyRef.Ref) + return newUnresolvedRef(propertyRef.Ref, propertyRef.Origin) } if err := p.visitJSON(settings, v); err != nil { if settings.failfast { @@ -2136,6 +3054,9 @@ func markSchemaErrorKey(err error, key string) error { if me, ok := unwrapped.(multiErrorForOneOf); ok { _ = markSchemaErrorKey(MultiError(me), key) } + if me, ok := unwrapped.(multiErrorForAllOf); ok { + _ = markSchemaErrorKey(MultiError(me), key) + } } } return v @@ -2174,9 +3095,9 @@ func (err *SchemaError) Error() string { if len(err.reversePath) > 0 { buf.WriteString(`Error at "`) reversePath := err.reversePath - for i := len(reversePath) - 1; i >= 0; i-- { + for _, r := range slices.Backward(reversePath) { buf.WriteByte('/') - buf.WriteString(reversePath[i]) + buf.WriteString(r) } buf.WriteString(`": `) } @@ -2247,8 +3168,27 @@ func unsupportedFormat(format string) error { return fmt.Errorf("unsupported 'format' value %q", format) } +func deepCopyJSONValue(v any) any { + switch v := v.(type) { + case map[string]any: + cp := make(map[string]any, len(v)) + for k, val := range v { + cp[k] = deepCopyJSONValue(val) + } + return cp + case []any: + cp := make([]any, 0, len(v)) + for _, val := range v { + cp = append(cp, deepCopyJSONValue(val)) + } + return cp + default: + return v // string, float64, bool, nil — all immutable + } +} + // UnmarshalJSON sets Schemas to a copy of data. func (schemas *Schemas) UnmarshalJSON(data []byte) (err error) { - *schemas, _, err = unmarshalStringMapP[SchemaRef](data) + *schemas, err = unmarshalStringMapP[SchemaRef](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go b/vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go index 489105b48..5053510a3 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go @@ -7,18 +7,19 @@ import ( "regexp" ) -type ( - // FormatValidator is an interface for custom format validators. - FormatValidator[T any] interface { - Validate(value T) error - } - // StringFormatValidator is a type alias for FormatValidator[string] - StringFormatValidator = FormatValidator[string] - // NumberFormatValidator is a type alias for FormatValidator[float64] - NumberFormatValidator = FormatValidator[float64] - // IntegerFormatValidator is a type alias for FormatValidator[int64] - IntegerFormatValidator = FormatValidator[int64] -) +// FormatValidator is an interface for custom format validators. +type FormatValidator[T any] interface { + Validate(value T) error +} + +// StringFormatValidator is a type alias for FormatValidator[string] +type StringFormatValidator = FormatValidator[string] + +// NumberFormatValidator is a type alias for FormatValidator[float64] +type NumberFormatValidator = FormatValidator[float64] + +// IntegerFormatValidator is a type alias for FormatValidator[int64] +type IntegerFormatValidator = FormatValidator[int64] var ( // SchemaStringFormats is a map of custom string format validators. @@ -44,7 +45,7 @@ const ( FormatOfStringDate = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])$` // FormatOfStringDateTime is a RFC3339 date-time format regexp, for example "2017-07-21T17:32:28Z". - FormatOfStringDateTime = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])T([0-1][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$` + FormatOfStringDateTime = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])T([0-1][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})$` ) func init() { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/schema_jsonschema_validator.go b/vendor/github.com/getkin/kin-openapi/openapi3/schema_jsonschema_validator.go new file mode 100644 index 000000000..d610e535d --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/schema_jsonschema_validator.go @@ -0,0 +1,212 @@ +package openapi3 + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// jsonSchemaValidator wraps the santhosh-tekuri/jsonschema validator +type jsonSchemaValidator struct { + compiler *jsonschema.Compiler + schema *jsonschema.Schema +} + +// newJSONSchemaValidator creates a new validator using JSON Schema 2020-12 +func newJSONSchemaValidator(schema *Schema) (*jsonSchemaValidator, error) { + // Convert OpenAPI Schema to JSON Schema format + schemaBytes, err := json.Marshal(schema) + if err != nil { + return nil, fmt.Errorf("failed to marshal schema: %w", err) + } + + var schemaMap map[string]any + if err := json.Unmarshal(schemaBytes, &schemaMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal schema: %w", err) + } + + // OpenAPI 3.1 specific transformations + transformOpenAPIToJSONSchema(schemaMap) + + // Create compiler + compiler := jsonschema.NewCompiler() + compiler.DefaultDraft(jsonschema.Draft2020) + + // Add the schema + schemaURL := "https://example.com/schema.json" + if err := compiler.AddResource(schemaURL, schemaMap); err != nil { + return nil, fmt.Errorf("failed to add schema resource: %w", err) + } + + // Compile the schema + compiledSchema, err := compiler.Compile(schemaURL) + if err != nil { + return nil, fmt.Errorf("failed to compile schema: %w", err) + } + + return &jsonSchemaValidator{ + compiler: compiler, + schema: compiledSchema, + }, nil +} + +// transformOpenAPIToJSONSchema converts OpenAPI 3.0/3.1 specific keywords to JSON Schema format +func transformOpenAPIToJSONSchema(schema map[string]any) { + // Handle nullable - in OpenAPI 3.0, nullable is a boolean flag + // In OpenAPI 3.1 / JSON Schema 2020-12, we use type arrays + if nullable, ok := schema["nullable"].(bool); ok && nullable { + if typeVal, ok := schema["type"].(string); ok { + // Convert to type array with null + schema["type"] = []string{typeVal, "null"} + } else if _, hasType := schema["type"]; !hasType { + // nullable: true without type - add "null" to allow null values + schema["type"] = []string{"null"} + } + delete(schema, "nullable") + } + + // Handle exclusiveMinimum/exclusiveMaximum + // In OpenAPI 3.0, these are booleans alongside minimum/maximum + // In JSON Schema 2020-12, they are numeric values + if exclusiveMin, ok := schema["exclusiveMinimum"].(bool); ok { + if exclusiveMin { + if schemaMin, ok := schema["minimum"].(float64); ok { + schema["exclusiveMinimum"] = schemaMin + delete(schema, "minimum") + } else { + delete(schema, "exclusiveMinimum") + } + } else { + // exclusiveMinimum: false means inclusive, which is the JSON Schema default + delete(schema, "exclusiveMinimum") + } + } + if exclusiveMax, ok := schema["exclusiveMaximum"].(bool); ok { + if exclusiveMax { + if schemaMax, ok := schema["maximum"].(float64); ok { + schema["exclusiveMaximum"] = schemaMax + delete(schema, "maximum") + } else { + delete(schema, "exclusiveMaximum") + } + } else { + // exclusiveMaximum: false means inclusive, which is the JSON Schema default + delete(schema, "exclusiveMaximum") + } + } + + // Remove OpenAPI-specific keywords that aren't in JSON Schema + delete(schema, "discriminator") + delete(schema, "xml") + delete(schema, "externalDocs") + delete(schema, "example") // Use "examples" in 2020-12 + + // Recursively transform nested schemas (single schema fields) + for _, key := range []string{ + "additionalProperties", "items", "not", + // OpenAPI 3.1 / JSON Schema 2020-12 fields + "contains", "propertyNames", "unevaluatedItems", "unevaluatedProperties", + "if", "then", "else", "contentSchema", + } { + if val, ok := schema[key]; ok { + if nestedSchema, ok := val.(map[string]any); ok { + transformOpenAPIToJSONSchema(nestedSchema) + } + } + } + + // Transform schema arrays (oneOf, anyOf, allOf, prefixItems) + for _, key := range []string{"oneOf", "anyOf", "allOf", "prefixItems"} { + if val, ok := schema[key].([]any); ok { + for _, item := range val { + if nestedSchema, ok := item.(map[string]any); ok { + transformOpenAPIToJSONSchema(nestedSchema) + } + } + } + } + + // Transform schema maps (properties, patternProperties, dependentSchemas, $defs) + for _, key := range []string{"properties", "patternProperties", "dependentSchemas", "$defs"} { + if props, ok := schema[key].(map[string]any); ok { + for _, propVal := range props { + if propSchema, ok := propVal.(map[string]any); ok { + transformOpenAPIToJSONSchema(propSchema) + } + } + } + } +} + +// validate validates a value against the compiled JSON Schema +func (v *jsonSchemaValidator) validate(value any) error { + if err := v.schema.Validate(value); err != nil { + // Convert jsonschema error to SchemaError + return convertJSONSchemaError(err) + } + return nil +} + +// convertJSONSchemaError converts a jsonschema validation error to OpenAPI SchemaError format +func convertJSONSchemaError(err error) error { + // TODO: Go 1.26 + // if err, ok := errors.AsType[*jsonschema.ValidationError](err); ok { + // return formatValidationError(err, "") + var validationErr *jsonschema.ValidationError + if errors.As(err, &validationErr) { + return formatValidationError(validationErr, "") + } + return err +} + +// formatValidationError recursively formats validation errors +func formatValidationError(verr *jsonschema.ValidationError, parentPath string) error { + // Build the path from InstanceLocation slice + path := "/" + strings.Join(verr.InstanceLocation, "/") + if parentPath != "" && path != "/" { + path = parentPath + path + } else if path == "/" { + path = parentPath + } + + // Build error message using the Error() method + var msg strings.Builder + if path != "" { + fmt.Fprintf(&msg, `error at "%s": `, path) + } + msg.WriteString(verr.Error()) + + // If there are sub-errors, format them too + if len(verr.Causes) > 0 { + var subErrors MultiError + for _, cause := range verr.Causes { + if subErr := formatValidationError(cause, path); subErr != nil { + subErrors = append(subErrors, subErr) + } + } + if len(subErrors) > 0 { + return &SchemaError{ + Reason: msg.String(), + Origin: fmt.Errorf("validation failed due to: %w", subErrors), + } + } + } + + return &SchemaError{ + Reason: msg.String(), + } +} + +// useJSONSchema2020 validates using the JSON Schema 2020-12 validator +func (schema *Schema) useJSONSchema2020(settings *schemaValidationSettings, value any) error { + validator, err := newJSONSchemaValidator(schema) + if err != nil { + // Fall back to built-in validator if compilation fails + return schema.visitJSON(settings, value) + } + + return validator.validate(value) +} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/schema_pattern.go b/vendor/github.com/getkin/kin-openapi/openapi3/schema_pattern.go index 581971378..0be88955d 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/schema_pattern.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/schema_pattern.go @@ -21,15 +21,16 @@ func (schema *Schema) compilePattern(c RegexCompilerFunc) (cp RegexMatcher, err cp, err = regexp.Compile(intoGoRegexp(pattern)) } if err != nil { - err = &SchemaError{ + schemaErr := &SchemaError{ Schema: schema, SchemaField: "pattern", Origin: err, Reason: fmt.Sprintf("cannot compile pattern %q: %v", pattern, err), } + err = newSchemaPatternRegexError(pattern, schemaErr, schema.Origin) return } - var _ bool = compiledPatterns.CompareAndSwap(pattern, nil, cp) + compiledPatterns.Store(pattern, cp) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/schema_validation_settings.go b/vendor/github.com/getkin/kin-openapi/openapi3/schema_validation_settings.go index e9c1422bd..205d95d8f 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/schema_validation_settings.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/schema_validation_settings.go @@ -21,6 +21,7 @@ type schemaValidationSettings struct { patternValidationDisabled bool readOnlyValidationDisabled bool writeOnlyValidationDisabled bool + useJSONSchema2020 bool // Use JSON Schema 2020-12 validator for OpenAPI 3.1 regexCompiler RegexCompilerFunc @@ -28,6 +29,11 @@ type schemaValidationSettings struct { defaultsSet func() customizeMessageError func(err *SchemaError) string + + // Per-validation format validators (checked before global ones) + stringFormats map[string]StringFormatValidator + numberFormats map[string]NumberFormatValidator + integerFormats map[string]IntegerFormatValidator } // FailFast returns schema validation errors quicker. @@ -83,6 +89,76 @@ func SetSchemaRegexCompiler(c RegexCompilerFunc) SchemaValidationOption { return func(s *schemaValidationSettings) { s.regexCompiler = c } } +// WithStringFormatValidators adds per-validation string format validators. +// These validators are checked before global SchemaStringFormats and allow +// different validations for the same format name across different specs. +func WithStringFormatValidators(validators map[string]StringFormatValidator) SchemaValidationOption { + return func(s *schemaValidationSettings) { + s.stringFormats = validators + } +} + +// WithStringFormatValidator adds a single per-validation string format validator. +// This validator is checked before global SchemaStringFormats and allows +// different validations for the same format name across different specs. +func WithStringFormatValidator(name string, validator StringFormatValidator) SchemaValidationOption { + return func(s *schemaValidationSettings) { + if s.stringFormats == nil { + s.stringFormats = make(map[string]StringFormatValidator) + } + s.stringFormats[name] = validator + } +} + +// WithNumberFormatValidators adds per-validation number format validators. +// These validators are checked before global SchemaNumberFormats and allow +// different validations for the same format name across different specs. +func WithNumberFormatValidators(validators map[string]NumberFormatValidator) SchemaValidationOption { + return func(s *schemaValidationSettings) { + s.numberFormats = validators + } +} + +// WithNumberFormatValidator adds a single per-validation number format validator. +// This validator is checked before global SchemaNumberFormats and allows +// different validations for the same format name across different specs. +func WithNumberFormatValidator(name string, validator NumberFormatValidator) SchemaValidationOption { + return func(s *schemaValidationSettings) { + if s.numberFormats == nil { + s.numberFormats = make(map[string]NumberFormatValidator) + } + s.numberFormats[name] = validator + } +} + +// WithIntegerFormatValidators adds per-validation integer format validators. +// These validators are checked before global SchemaIntegerFormats and allow +// different validations for the same format name across different specs. +func WithIntegerFormatValidators(validators map[string]IntegerFormatValidator) SchemaValidationOption { + return func(s *schemaValidationSettings) { + s.integerFormats = validators + } +} + +// WithIntegerFormatValidator adds a single per-validation integer format validator. +// This validator is checked before global SchemaIntegerFormats and allows +// different validations for the same format name across different specs. +func WithIntegerFormatValidator(name string, validator IntegerFormatValidator) SchemaValidationOption { + return func(s *schemaValidationSettings) { + if s.integerFormats == nil { + s.integerFormats = make(map[string]IntegerFormatValidator) + } + s.integerFormats[name] = validator + } +} + +// EnableJSONSchema2020 enables JSON Schema 2020-12 compliant validation. +// This enables support for OpenAPI 3.1 and JSON Schema 2020-12 features. +// When enabled, validation uses the jsonschema library instead of the built-in validator. +func EnableJSONSchema2020() SchemaValidationOption { + return func(s *schemaValidationSettings) { s.useJSONSchema2020 = true } +} + func newSchemaValidationSettings(opts ...SchemaValidationOption) *schemaValidationSettings { settings := &schemaValidationSettings{} for _, opt := range opts { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/security_requirements.go b/vendor/github.com/getkin/kin-openapi/openapi3/security_requirements.go index 6af80e8b3..36b6401b1 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/security_requirements.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/security_requirements.go @@ -45,13 +45,13 @@ func (security SecurityRequirement) Authenticate(provider string, scopes ...stri // Validate returns an error if SecurityRequirement does not comply with the OpenAPI spec. func (security *SecurityRequirement) Validate(ctx context.Context, opts ...ValidationOption) error { - ctx = WithValidationOptions(ctx, opts...) + // ctx = WithValidationOptions(ctx, opts...) return nil } // UnmarshalJSON sets SecurityRequirement to a copy of data. func (security *SecurityRequirement) UnmarshalJSON(data []byte) (err error) { - *security, _, err = unmarshalStringMap[[]string](data) + *security, err = unmarshalStringMap[[]string](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go b/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go index 676002aa5..efe7f20bb 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go @@ -3,16 +3,18 @@ package openapi3 import ( "context" "encoding/json" - "errors" "fmt" + "maps" "net/url" + "slices" ) // SecurityScheme is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-scheme-object +// and https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#security-scheme-object type SecurityScheme struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Type string `json:"type,omitempty" yaml:"type,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -63,9 +65,7 @@ func (ss SecurityScheme) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of SecurityScheme. func (ss SecurityScheme) MarshalYAML() (any, error) { m := make(map[string]any, 8+len(ss.Extensions)) - for k, v := range ss.Extensions { - m[k] = v - } + maps.Copy(m, ss.Extensions) if x := ss.Type; x != "" { m["type"] = x } @@ -101,7 +101,6 @@ func (ss *SecurityScheme) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "type") delete(x.Extensions, "description") delete(x.Extensions, "name") @@ -164,16 +163,20 @@ func (ss *SecurityScheme) Validate(ctx context.Context, opts ...ValidationOption hasBearerFormat = true case "basic", "negotiate", "digest": default: - return fmt.Errorf("security scheme of type 'http' has invalid 'scheme' value %q", scheme) + return newInvalidHTTPScheme(scheme, ss.Origin) } case "oauth2": hasFlow = true case "openIdConnect": if ss.OpenIdConnectUrl == "" { - return fmt.Errorf("no OIDC URL found for openIdConnect security scheme %q", ss.Name) + return newOpenIDConnectURLRequired(ss.Name, ss.Origin) + } + case "mutualTLS": + if !getValidationOptions(ctx).isOpenAPI31OrLater { + return errValueOfFieldFor31Plus(ss.Type, "type") } default: - return fmt.Errorf("security scheme 'type' can't be %q", ss.Type) + return newInvalidSecuritySchemeType(ss.Type, ss.Origin) } // Validate "in" and "name" @@ -181,44 +184,44 @@ func (ss *SecurityScheme) Validate(ctx context.Context, opts ...ValidationOption switch ss.In { case "query", "header", "cookie": default: - return fmt.Errorf("security scheme of type 'apiKey' should have 'in'. It can be 'query', 'header' or 'cookie', not %q", ss.In) + return newAPIKeyInInvalid(ss.In, ss.Origin) } if ss.Name == "" { - return errors.New("security scheme of type 'apiKey' should have 'name'") + return newAPIKeySecuritySchemeNameRequired(ss.Origin) } } else if len(ss.In) > 0 { - return fmt.Errorf("security scheme of type %q can't have 'in'", ss.Type) + return newSecuritySchemeInForbidden(ss.Type, ss.Origin) } else if len(ss.Name) > 0 { - return fmt.Errorf("security scheme of type %q can't have 'name'", ss.Type) + return newSecuritySchemeNameForbidden(ss.Type, ss.Origin) } // Validate "format" // "bearerFormat" is an arbitrary string so we only check if the scheme supports it if !hasBearerFormat && len(ss.BearerFormat) > 0 { - return fmt.Errorf("security scheme of type %q can't have 'bearerFormat'", ss.Type) + return newSecuritySchemeBearerFormatForbidden(ss.Type, ss.Origin) } // Validate "flow" if hasFlow { flow := ss.Flows if flow == nil { - return fmt.Errorf("security scheme of type %q should have 'flows'", ss.Type) + return newSecuritySchemeFlowsRequired(ss.Type, ss.Origin) } if err := flow.Validate(ctx); err != nil { - return fmt.Errorf("security scheme 'flow' is invalid: %w", err) + return &SecuritySchemeFlowValidationError{Cause: err} } } else if ss.Flows != nil { - return fmt.Errorf("security scheme of type %q can't have 'flows'", ss.Type) + return newSecuritySchemeFlowsForbidden(ss.Type, ss.Origin) } - return validateExtensions(ctx, ss.Extensions) + return validateExtensions(ctx, ss.Extensions, ss.Origin) } // OAuthFlows is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flows-object type OAuthFlows struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"` Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"` @@ -247,9 +250,7 @@ func (flows OAuthFlows) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of OAuthFlows. func (flows OAuthFlows) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(flows.Extensions)) - for k, v := range flows.Extensions { - m[k] = v - } + maps.Copy(m, flows.Extensions) if x := flows.Implicit; x != nil { m["implicit"] = x } @@ -273,7 +274,6 @@ func (flows *OAuthFlows) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "implicit") delete(x.Extensions, "password") delete(x.Extensions, "clientCredentials") @@ -291,41 +291,41 @@ func (flows *OAuthFlows) Validate(ctx context.Context, opts ...ValidationOption) if v := flows.Implicit; v != nil { if err := v.validate(ctx, oAuthFlowTypeImplicit, opts...); err != nil { - return fmt.Errorf("the OAuth flow 'implicit' is invalid: %w", err) + return &OAuthFlowValidationError{FlowKind: "implicit", Cause: err} } } if v := flows.Password; v != nil { if err := v.validate(ctx, oAuthFlowTypePassword, opts...); err != nil { - return fmt.Errorf("the OAuth flow 'password' is invalid: %w", err) + return &OAuthFlowValidationError{FlowKind: "password", Cause: err} } } if v := flows.ClientCredentials; v != nil { if err := v.validate(ctx, oAuthFlowTypeClientCredentials, opts...); err != nil { - return fmt.Errorf("the OAuth flow 'clientCredentials' is invalid: %w", err) + return &OAuthFlowValidationError{FlowKind: "clientCredentials", Cause: err} } } if v := flows.AuthorizationCode; v != nil { if err := v.validate(ctx, oAuthFlowAuthorizationCode, opts...); err != nil { - return fmt.Errorf("the OAuth flow 'authorizationCode' is invalid: %w", err) + return &OAuthFlowValidationError{FlowKind: "authorizationCode", Cause: err} } } - return validateExtensions(ctx, flows.Extensions) + return validateExtensions(ctx, flows.Extensions, flows.Origin) } // OAuthFlow is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flow-object type OAuthFlow struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` - AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` - TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` - RefreshURL string `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"` - Scopes StringMap `json:"scopes" yaml:"scopes"` // required + AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` + TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` + RefreshURL string `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"` + Scopes StringMap[string] `json:"scopes" yaml:"scopes"` // required } // MarshalJSON returns the JSON encoding of OAuthFlow. @@ -340,9 +340,7 @@ func (flow OAuthFlow) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of OAuthFlow. func (flow OAuthFlow) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(flow.Extensions)) - for k, v := range flow.Extensions { - m[k] = v - } + maps.Copy(m, flow.Extensions) if x := flow.AuthorizationURL; x != "" { m["authorizationUrl"] = x } @@ -365,7 +363,6 @@ func (flow *OAuthFlow) UnmarshalJSON(data []byte) error { } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "authorizationUrl") delete(x.Extensions, "tokenUrl") delete(x.Extensions, "refreshUrl") @@ -383,35 +380,30 @@ func (flow *OAuthFlow) Validate(ctx context.Context, opts ...ValidationOption) e if v := flow.RefreshURL; v != "" { if _, err := url.Parse(v); err != nil { - return fmt.Errorf("field 'refreshUrl' is invalid: %w", err) + return &OAuthFlowFieldValidationError{Field: "refreshUrl", Cause: err} } } if flow.Scopes == nil { - return errors.New("field 'scopes' is missing") + return newOAuthFlowScopesRequired(flow.Origin) } - return validateExtensions(ctx, flow.Extensions) + return validateExtensions(ctx, flow.Extensions, flow.Origin) } func (flow *OAuthFlow) validate(ctx context.Context, typ oAuthFlowType, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) typeIn := func(types ...oAuthFlowType) bool { - for _, ty := range types { - if ty == typ { - return true - } - } - return false + return slices.Contains(types, typ) } if in := typeIn(oAuthFlowTypeImplicit, oAuthFlowAuthorizationCode); true { switch { case flow.AuthorizationURL == "" && in: - return errors.New("field 'authorizationUrl' is empty or missing") + return newOAuthFlowAuthorizationURLRequired(flow.Origin) case flow.AuthorizationURL != "" && !in: - return errors.New("field 'authorizationUrl' should not be set") + return newOAuthFlowAuthorizationURLForbidden(flow.Origin) case flow.AuthorizationURL != "": if _, err := url.Parse(flow.AuthorizationURL); err != nil { return fmt.Errorf("field 'authorizationUrl' is invalid: %w", err) @@ -422,9 +414,9 @@ func (flow *OAuthFlow) validate(ctx context.Context, typ oAuthFlowType, opts ... if in := typeIn(oAuthFlowTypePassword, oAuthFlowTypeClientCredentials, oAuthFlowAuthorizationCode); true { switch { case flow.TokenURL == "" && in: - return errors.New("field 'tokenUrl' is empty or missing") + return newOAuthFlowTokenURLRequired(flow.Origin) case flow.TokenURL != "" && !in: - return errors.New("field 'tokenUrl' should not be set") + return newOAuthFlowTokenURLForbidden(flow.Origin) case flow.TokenURL != "": if _, err := url.Parse(flow.TokenURL); err != nil { return fmt.Errorf("field 'tokenUrl' is invalid: %w", err) @@ -437,6 +429,6 @@ func (flow *OAuthFlow) validate(ctx context.Context, typ oAuthFlowType, opts ... // UnmarshalJSON sets SecuritySchemes to a copy of data. func (securitySchemes *SecuritySchemes) UnmarshalJSON(data []byte) (err error) { - *securitySchemes, _, err = unmarshalStringMapP[SecuritySchemeRef](data) + *securitySchemes, err = unmarshalStringMapP[SecuritySchemeRef](data) return } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/server.go b/vendor/github.com/getkin/kin-openapi/openapi3/server.go index ea7751745..181eace24 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/server.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/server.go @@ -4,9 +4,8 @@ import ( "context" "encoding/json" "errors" - "fmt" + "maps" "net/url" - "sort" "strings" ) @@ -16,13 +15,14 @@ type Servers []*Server // Validate returns an error if Servers does not comply with the OpenAPI spec. func (servers Servers) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) for _, v := range servers { - if err := v.Validate(ctx); err != nil { + if err := me.emit(v.Validate(ctx)); err != nil { return err } } - return nil + return me.result() } // BasePath returns the base path of the first server in the list, or /. @@ -51,11 +51,11 @@ func (servers Servers) MatchURL(parsedURL *url.URL) (*Server, []string, string) // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-object type Server struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` - URL string `json:"url" yaml:"url"` // Required - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Variables map[string]*ServerVariable `json:"variables,omitempty" yaml:"variables,omitempty"` + URL string `json:"url" yaml:"url"` // Required + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Variables ServerVariables `json:"variables,omitempty" yaml:"variables,omitempty"` } // BasePath returns the base path extracted from the default values of variables, if any. @@ -66,8 +66,8 @@ func (server *Server) BasePath() (string, error) { } uri := server.URL - for name, svar := range server.Variables { - uri = strings.ReplaceAll(uri, "{"+name+"}", svar.Default) + for _, name := range componentNames(server.Variables) { + uri = strings.ReplaceAll(uri, "{"+name+"}", server.Variables[name].Default) } u, err := url.ParseRequestURI(uri) @@ -94,9 +94,7 @@ func (server Server) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Server. func (server Server) MarshalYAML() (any, error) { m := make(map[string]any, 3+len(server.Extensions)) - for k, v := range server.Extensions { - m[k] = v - } + maps.Copy(m, server.Extensions) m["url"] = server.URL if x := server.Description; x != "" { m["description"] = x @@ -115,13 +113,13 @@ func (server *Server) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "url") delete(x.Extensions, "description") delete(x.Extensions, "variables") if len(x.Extensions) == 0 { x.Extensions = nil } + delete(x.Variables, originKey) *server = Server(x) return nil } @@ -198,45 +196,63 @@ func (server Server) MatchRawURL(input string) ([]string, string, bool) { } // Validate returns an error if Server does not comply with the OpenAPI spec. -func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) (err error) { +func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) if server.URL == "" { - return errors.New("value of url must be a non-empty string") + if err := me.emit(newServerURLRequired(server.Origin)); err != nil { + return err + } } opening, closing := strings.Count(server.URL, "{"), strings.Count(server.URL, "}") if opening != closing { - return errors.New("server URL has mismatched { and }") + if err := me.emit(newServerURLMismatchedBraces(server.URL, server.Origin)); err != nil { + return err + } } if opening != len(server.Variables) { - return errors.New("server has undeclared variables") + if err := me.emit(newServerURLUndeclaredVariables(server.URL, server.Origin)); err != nil { + return err + } } - variables := make([]string, 0, len(server.Variables)) - for name := range server.Variables { - variables = append(variables, name) - } - sort.Strings(variables) - for _, name := range variables { + for _, name := range componentNames(server.Variables) { v := server.Variables[name] if !strings.Contains(server.URL, "{"+name+"}") { - return errors.New("server has undeclared variables") + if err := me.emit(newServerURLUndeclaredVariables(server.URL, server.Origin)); err != nil { + return err + } + // Variable name doesn't appear in the URL template; descending + // into its Validate would surface findings under a variable + // the URL never references, with no resolution path until the + // URL is fixed. + continue } - if err = v.Validate(ctx); err != nil { - return + if err := me.emit(v.Validate(ctx)); err != nil { + return err } } - return validateExtensions(ctx, server.Extensions) + return me.finalize(validateExtensions(ctx, server.Extensions, server.Origin)) } // ServerVariable is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-variable-object +// ServerVariables is a map of ServerVariable objects keyed by variable name. +type ServerVariables map[string]*ServerVariable + +// UnmarshalJSON sets ServerVariables to a copy of data. +func (serverVariables *ServerVariables) UnmarshalJSON(data []byte) (err error) { + *serverVariables, err = unmarshalStringMapP[ServerVariable](data) + return +} + type ServerVariable struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"` Default string `json:"default,omitempty" yaml:"default,omitempty"` @@ -255,9 +271,7 @@ func (serverVariable ServerVariable) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of ServerVariable. func (serverVariable ServerVariable) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(serverVariable.Extensions)) - for k, v := range serverVariable.Extensions { - m[k] = v - } + maps.Copy(m, serverVariable.Extensions) if x := serverVariable.Enum; len(x) != 0 { m["enum"] = x } @@ -278,7 +292,6 @@ func (serverVariable *ServerVariable) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "enum") delete(x.Extensions, "default") delete(x.Extensions, "description") @@ -298,8 +311,8 @@ func (serverVariable *ServerVariable) Validate(ctx context.Context, opts ...Vali if err != nil { return err } - return fmt.Errorf("field default is required in %s", data) + return newServerVariableDefaultRequired(string(data), serverVariable.Origin) } - return validateExtensions(ctx, serverVariable.Extensions) + return validateExtensions(ctx, serverVariable.Extensions, serverVariable.Origin) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/stringmap.go b/vendor/github.com/getkin/kin-openapi/openapi3/stringmap.go index d1b91ac94..f416899e7 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/stringmap.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/stringmap.go @@ -3,60 +3,50 @@ package openapi3 import "encoding/json" // StringMap is a map[string]string that ignores the origin in the underlying json representation. -type StringMap map[string]string +type StringMap[V any] map[string]V // UnmarshalJSON sets StringMap to a copy of data. -func (stringMap *StringMap) UnmarshalJSON(data []byte) (err error) { - *stringMap, _, err = unmarshalStringMap[string](data) +func (stringMap *StringMap[V]) UnmarshalJSON(data []byte) (err error) { + *stringMap, err = unmarshalStringMap[V](data) return } // unmarshalStringMapP unmarshals given json into a map[string]*V -func unmarshalStringMapP[V any](data []byte) (map[string]*V, *Origin, error) { +func unmarshalStringMapP[V any](data []byte) (map[string]*V, error) { var m map[string]any if err := json.Unmarshal(data, &m); err != nil { - return nil, nil, err - } - - origin, err := popOrigin(m, originKey) - if err != nil { - return nil, nil, err + return nil, err } result := make(map[string]*V, len(m)) - for k, v := range m { - value, err := deepCast[V](v) + for _, k := range componentNames(m) { + value, err := deepCast[V](m[k]) if err != nil { - return nil, nil, err + return nil, err } result[k] = value } - return result, origin, nil + return result, nil } // unmarshalStringMap unmarshals given json into a map[string]V -func unmarshalStringMap[V any](data []byte) (map[string]V, *Origin, error) { +func unmarshalStringMap[V any](data []byte) (map[string]V, error) { var m map[string]any if err := json.Unmarshal(data, &m); err != nil { - return nil, nil, err - } - - origin, err := popOrigin(m, originKey) - if err != nil { - return nil, nil, err + return nil, err } result := make(map[string]V, len(m)) - for k, v := range m { - value, err := deepCast[V](v) + for _, k := range componentNames(m) { + value, err := deepCast[V](m[k]) if err != nil { - return nil, nil, err + return nil, err } result[k] = *value } - return result, origin, nil + return result, nil } // deepCast casts any value to a value of type V. @@ -72,17 +62,3 @@ func deepCast[V any](value any) (*V, error) { } return &result, nil } - -// popOrigin removes the origin from the map and returns it. -func popOrigin(m map[string]any, key string) (*Origin, error) { - if !IncludeOrigin { - return nil, nil - } - - origin, err := deepCast[Origin](m[key]) - if err != nil { - return nil, err - } - delete(m, key) - return origin, nil -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/tag.go b/vendor/github.com/getkin/kin-openapi/openapi3/tag.go index 841161ec0..55b302fa5 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/tag.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/tag.go @@ -3,7 +3,7 @@ package openapi3 import ( "context" "encoding/json" - "fmt" + "maps" ) // Tags is specified by OpenAPI/Swagger 3.0 standard. @@ -21,20 +21,34 @@ func (tags Tags) Get(name string) *Tag { // Validate returns an error if Tags does not comply with the OpenAPI spec. func (tags Tags) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) + + // Each tag name in the list MUST be unique (OpenAPI Object). Empty names + // are left to per-tag validation, not flagged as duplicates here. + seen := make(map[string]struct{}, len(tags)) for _, v := range tags { - if err := v.Validate(ctx); err != nil { + if v.Name != "" { + if _, dup := seen[v.Name]; dup { + if err := me.emit(&DuplicateTagError{Name: v.Name, Origin: v.Origin}); err != nil { + return err + } + } + seen[v.Name] = struct{}{} + } + wrap := func(e error) error { return &TagValidationError{Name: v.Name, Cause: e} } + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { return err } } - return nil + return me.result() } // Tag is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#tag-object type Tag struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -53,9 +67,7 @@ func (t Tag) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of Tag. func (t Tag) MarshalYAML() (any, error) { m := make(map[string]any, 3+len(t.Extensions)) - for k, v := range t.Extensions { - m[k] = v - } + maps.Copy(m, t.Extensions) if x := t.Name; x != "" { m["name"] = x } @@ -76,7 +88,6 @@ func (t *Tag) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "description") delete(x.Extensions, "externalDocs") @@ -90,12 +101,14 @@ func (t *Tag) UnmarshalJSON(data []byte) error { // Validate returns an error if Tag does not comply with the OpenAPI spec. func (t *Tag) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + me := newErrCollector(ctx) if v := t.ExternalDocs; v != nil { - if err := v.Validate(ctx); err != nil { - return fmt.Errorf("invalid external docs: %w", err) + wrap := func(e error) error { return &SectionValidationError{Section: "external docs", Cause: e} } + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { + return err } } - return validateExtensions(ctx, t.Extensions) + return me.finalize(validateExtensions(ctx, t.Extensions, t.Origin)) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/validation_error.go b/vendor/github.com/getkin/kin-openapi/openapi3/validation_error.go new file mode 100644 index 000000000..3bdb72dd0 --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/validation_error.go @@ -0,0 +1,1601 @@ +package openapi3 + +import "fmt" + +// ValidationError is the embedded base for every typed validation error +// emitted by the document validation walker (T.Validate, Info.Validate, +// Paths.Validate, etc.). Four categories of typed error are exposed; +// pick whichever the caller needs: +// +// 1. Base — *ValidationError. Catchall for "this is a validation +// issue, here is the message". Reachable from any leaf via the As +// method that each leaf implements. +// 2. Cluster — types like *RequiredFieldError or +// *FieldVersionMismatchError. Group families of related failures +// and expose the family-level metadata (Field, MinVersion, ...). +// Wrap the underlying leaf via Unwrap, so errors.As can still walk +// to the leaf. Some clusters are single-site (e.g. *SchemaTypeError) +// and carry only their own fields with no separate leaf. +// 3. Leaf — one type per call site (e.g. *InfoVersionRequired, +// *LicenseIdentifierFieldFor31Plus). Lets callers match an exact +// failure point without string comparison. +// 4. Context wrapper — types like *SectionValidationError, +// *PathValidationError, *ParameterFieldValidationError. Add scope +// ("which section", "which path", "which parameter") around an +// inner error chain but do NOT themselves report a failure +// condition — the actual error lives in Cause. Defined in +// validation_error_context.go; see that file's header for the +// full inventory and conventions. +// +// All four are reachable from the same returned error through +// standard Go error wrapping (errors.As, errors.Is, errors.Unwrap), +// so a caller that only needs "is it a validation error?" stops at +// the base and a caller that wants "is it specifically license.identifier +// being used in 3.0?" matches the leaf. A caller that wants "which +// section did this happen in?" matches the context wrapper and walks +// further for the cluster/leaf. +// +// A canonical error chain therefore looks like: +// +// ComponentValidationError{Section: "schema", Name: "Foo"} +// -> RequiredFieldError{Field: "type"} +// -> SchemaTypeRequired{Message: "..."} +// +// Context wrapper carries WHERE, cluster carries WHAT category, leaf +// carries EXACTLY WHICH case. +// +// Backward compatibility: every site that today returns errors.New(msg) +// migrates to a leaf type that embeds ValidationError with Message set +// to the original string. (*ValidationError).Error() returns Message +// unchanged, so existing string-matching consumers see identical output. +type ValidationError struct { + Message string +} + +func (e *ValidationError) Error() string { return e.Message } + +// asValidationError is a small helper used by every leaf type's As +// method to expose the embedded *ValidationError to errors.As. Defined +// once here so the leaf-side boilerplate stays a single line. +func asValidationError(target any, ve *ValidationError) bool { + t, ok := target.(**ValidationError) + if !ok { + return false + } + *t = ve + return true +} + +// --------------------------------------------------------------------- +// Cluster types — group families of related failures. +// --------------------------------------------------------------------- + +// RequiredFieldError clusters "X must be a non-empty value" failures +// across the spec (info.title, info.version, license.name, the openapi +// version string, server.url). Carries the field path, wraps the +// per-site leaf so callers can match either: +// +// var rfe *RequiredFieldError +// if errors.As(err, &rfe) { /* knows the field */ } +// +// var ivr *InfoVersionRequired +// if errors.As(err, &ivr) { /* knows it's exactly info.version */ } +type RequiredFieldError struct { + // Field is the JSON-pointer-style path of the required field + // (e.g. "info.version", "license.name", "openapi", "server.url"). + Field string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. Nil for + // document-root fields (Loader doesn't track Origin on *T) and on + // loads where origin tracking was off. + Origin *Origin +} + +func (e *RequiredFieldError) Error() string { return e.Cause.Error() } +func (e *RequiredFieldError) Unwrap() error { return e.Cause } + +// SchemaValueError clusters failures of "this schema's value +// doesn't satisfy the schema's own constraints" — example, default, +// examples[i], etc. checked against the schema during document +// validation. Wraps the underlying error from VisitJSON (a +// *SchemaError or a MultiError of them) so callers can match either: +// +// var sve *SchemaValueError +// if errors.As(err, &sve) { /* knows ValueKind = "example" */ } +// +// var se *SchemaError +// if errors.As(err, &se) { /* full schema-validation detail */ } +// +// Cause is typed as error (not *SchemaError) because VisitJSON can +// return either a single SchemaError or a MultiError aggregating +// several. errors.As walks both shapes transparently. +type SchemaValueError struct { + // ValueKind identifies the schema sub-field whose value failed + // (e.g. "example", "default"). + ValueKind string + // Cause is the underlying error from schema.VisitJSON — either a + // *SchemaError or a MultiError of them. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. Nil when + // origin tracking is off. + Origin *Origin +} + +func (e *SchemaValueError) Error() string { + return fmt.Sprintf("invalid %s: %s", e.ValueKind, e.Cause.Error()) +} + +func (e *SchemaValueError) Unwrap() error { return e.Cause } + +// PathParametersError clusters "operation declares fewer/more path +// parameters than appear in the path template" failures. Carries the +// path template, method, and the list of missing parameter names so +// callers can render or filter without parsing the message. +type PathParametersError struct { + // Path is the path template (e.g. "/api/{domain}/{project}/..."). + Path string + // Method is the HTTP method (e.g. "POST"). + Method string + // Missing names path-template variables (or operation parameters) + // that don't have a corresponding declaration on the other side. + Missing []string + // Origin is the source location of the path item when the document + // was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *PathParametersError) Error() string { + return fmt.Sprintf("operation %s %s must define exactly all path parameters (missing: %v)", + e.Method, e.Path, e.Missing) +} + +// FieldVersionMismatchError clusters "field X is for OpenAPI >=Y" +// failures (3.1+ keywords used in 3.0 documents). Carries the field +// name and minimum version, wraps the per-site leaf. +type FieldVersionMismatchError struct { + // Field is the field name flagged (e.g. "summary", "identifier", + // "$defs", "prefixItems", "contains", ...). + Field string + // MinVersion is the minimum OpenAPI version that allows the field + // (e.g. "3.1"). + MinVersion string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. Nil for + // document-root fields (Loader doesn't track Origin on *T) and on + // loads where origin tracking was off. + Origin *Origin +} + +func (e *FieldVersionMismatchError) Error() string { return e.Cause.Error() } +func (e *FieldVersionMismatchError) Unwrap() error { return e.Cause } + +// ServerURLTemplateError clusters server URL template failures — +// mismatched braces and undeclared variables (template variables not +// matched by Server.Variables, or vice versa). +type ServerURLTemplateError struct { + // URL is the server URL whose template failed validation. + URL string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *ServerURLTemplateError) Error() string { return e.Cause.Error() } +func (e *ServerURLTemplateError) Unwrap() error { return e.Cause } + +// EitherFieldRequiredError clusters "at least one of these fields must +// be set" failures (example.value vs externalValue, link.operationId +// vs operationRef). +type EitherFieldRequiredError struct { + // Fields is the set of field names, at least one of which must be + // set (e.g. ["value", "externalValue"]). + Fields []string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *EitherFieldRequiredError) Error() string { return e.Cause.Error() } +func (e *EitherFieldRequiredError) Unwrap() error { return e.Cause } + +// SchemaBothFormsExclusive clusters "this union-typed schema field is +// set to both its boolean and schema forms simultaneously" failures — +// additionalProperties, unevaluatedItems, unevaluatedProperties. +type SchemaBothFormsExclusive struct { + // Field is the name of the union-typed schema property + // (e.g. "additionalProperties", "unevaluatedItems"). + Field string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *SchemaBothFormsExclusive) Error() string { return e.Cause.Error() } +func (e *SchemaBothFormsExclusive) Unwrap() error { return e.Cause } + +// ExactlyOneFieldError clusters "exactly one of these fields must be +// set" failures — e.g. a parameter or header where neither content +// nor schema is set, or both are. +type ExactlyOneFieldError struct { + // Fields is the set of fields, exactly one of which must be set + // (e.g. ["content", "schema"]). + Fields []string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *ExactlyOneFieldError) Error() string { return e.Cause.Error() } +func (e *ExactlyOneFieldError) Unwrap() error { return e.Cause } + +// SingleEntryContentError clusters "the content map must contain at +// most one entry" failures (parameter.content, header.content). +type SingleEntryContentError struct { + // Subject is the kind of object whose Content map is too large + // ("parameter", "header"). + Subject string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *SingleEntryContentError) Error() string { return e.Cause.Error() } +func (e *SingleEntryContentError) Unwrap() error { return e.Cause } + +// WebhookNilError clusters "the value at webhook key X is nil" +// failures from T.Validate's webhook walk. Carries the offending +// key name. +type WebhookNilError struct { + // Name is the webhook key whose value was nil. + Name string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error +} + +func (e *WebhookNilError) Error() string { return e.Cause.Error() } +func (e *WebhookNilError) Unwrap() error { return e.Cause } + +// MutuallyExclusiveFieldsError clusters "fields X and Y are both set, +// only one is allowed" failures (example.value vs externalValue, +// mediaType.example vs examples, license.url vs identifier, +// link.operationId vs operationRef). Carries both field names and +// wraps the per-site leaf. +type MutuallyExclusiveFieldsError struct { + // Field1 and Field2 name the two fields the spec forbids setting + // together (e.g. "value", "externalValue"). + Field1 string + Field2 string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *MutuallyExclusiveFieldsError) Error() string { return e.Cause.Error() } +func (e *MutuallyExclusiveFieldsError) Unwrap() error { return e.Cause } + +// ForbiddenFieldError clusters "field X must not be set in this +// context" failures (header.name and header.in inside a Headers map, +// OAuth flow URLs that don't apply to the chosen flow type). +type ForbiddenFieldError struct { + // Field is the name of the forbidden field (e.g. "name", "in", + // "authorizationUrl", "tokenUrl"). + Field string + // Cause is the underlying leaf error. Walked by errors.Unwrap. + Cause error + // Origin is the source location of the offending element when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *ForbiddenFieldError) Error() string { return e.Cause.Error() } +func (e *ForbiddenFieldError) Unwrap() error { return e.Cause } + +// PathParameterRequiredError clusters "path parameter X must be required" +// failures: per the OpenAPI spec, every parameter with `in: path` must be +// declared with `required: true`. Carries the parameter name so callers +// can render or filter by it. +type PathParameterRequiredError struct { + // Param is the path-parameter name (e.g. "groupId"). + Param string + // Origin is the source location of the offending parameter when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *PathParameterRequiredError) Error() string { + return fmt.Sprintf("path parameter %q must be required", e.Param) +} + +// DuplicateOperationIDError clusters "two operations share an operationId" +// failures. operationIds must be unique across all paths in a document. +// Endpoints are rendered as " " (e.g. "POST /things"). +type DuplicateOperationIDError struct { + // OperationID is the duplicated operationId value. + OperationID string + // Endpoint1 / Endpoint2 are the two offending endpoints, in + // deterministic order (lexicographically) for stable error messages. + Endpoint1 string + Endpoint2 string + // Origin is the source location of the second (offending) operation + // when the document was loaded with Loader.IncludeOrigin = true. The + // pre-existing Endpoint1 is implicitly fine; the duplicate landed at + // Endpoint2's site, which is the natural "go fix this" pointer. + Origin *Origin +} + +func (e *DuplicateOperationIDError) Error() string { + return fmt.Sprintf("operations %q and %q have the same operation id %q", + e.Endpoint1, e.Endpoint2, e.OperationID) +} + +// ExtraSiblingFieldsError clusters "unexpected sibling fields" failures. +// Most commonly this fires when fields appear alongside a $ref that the +// OpenAPI spec doesn't allow there, or as unknown keys on objects whose +// only permitted extras are `x-` extensions. Carries the offending field +// names so callers can render or filter. +type ExtraSiblingFieldsError struct { + // Fields is the list of unexpected sibling field names. + Fields []string + // Origin is the source location of the parent object that carries + // the extra siblings when the document was loaded with + // Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *ExtraSiblingFieldsError) Error() string { + return fmt.Sprintf("extra sibling fields: %+v", e.Fields) +} + +// SchemaTypeError clusters "unsupported 'type' value" failures on a +// Schema. Carries the bad type value so callers can surface it in +// user-facing output and filter findings by it. +type SchemaTypeError struct { + // Type is the rejected type value (e.g. "bool", "int", "http"). + Type string + // Origin is the source location of the offending schema when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *SchemaTypeError) Error() string { + return fmt.Sprintf("unsupported 'type' value %q", e.Type) +} + +// InvalidParameterInError clusters "parameter can't have 'in' value X" +// failures. The OpenAPI 3.x spec accepts only `path`, `query`, `header`, +// or `cookie`; this fires when a parameter declares anything else +// (commonly `body`, a Swagger 2.0 leftover). +type InvalidParameterInError struct { + // Value is the rejected `in:` value (e.g. "body", "formData"). + Value string + // Origin is the source location of the offending parameter when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *InvalidParameterInError) Error() string { + return fmt.Sprintf("parameter can't have 'in' value %q", e.Value) +} + +// SchemaPatternRegexError clusters "schema pattern failed to compile" +// failures. Kin's regex engine is Go's RE2, which rejects Perl features +// like `(?!...)` lookahead; specs that leak Perl regex patterns (common +// in AWS-style auto-generated schemas) trip this. Carries the offending +// pattern as a structured field for typed dispatch, while preserving +// the underlying SchemaError's rendered message byte-for-byte (Error() +// delegates to Cause.Error()) so existing string-based consumers and +// golden fixtures are unaffected. +type SchemaPatternRegexError struct { + // Pattern is the schema's `pattern:` value that failed to compile. + Pattern string + // Cause is the underlying error (a *SchemaError wrapping the + // regexp package's syntax error). Unwrap returns this so callers + // walking the error chain see the SchemaError. + Cause error + // Origin is the source location of the offending schema when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *SchemaPatternRegexError) Error() string { return e.Cause.Error() } + +func (e *SchemaPatternRegexError) Unwrap() error { return e.Cause } + +// InvalidSecuritySchemeTypeError clusters "security scheme 'type' can't +// be X" failures. The OpenAPI 3.x spec accepts only `apiKey`, `http`, +// `oauth2`, `openIdConnect`, and `mutualTLS` (3.1+); this fires when a +// security scheme declares anything else. +type InvalidSecuritySchemeTypeError struct { + // Type is the rejected type value (e.g. "cookie", "saml"). + Type string + // Origin is the source location of the offending security scheme + // when the document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *InvalidSecuritySchemeTypeError) Error() string { + return fmt.Sprintf("security scheme 'type' can't be %q", e.Type) +} + +// InvalidHTTPSchemeError clusters "security scheme of type 'http' has +// invalid 'scheme' value X" failures. The OpenAPI/HTTP-auth registry +// accepts only `bearer`, `basic`, `negotiate`, `digest`; this fires +// when an http scheme declares anything else. +type InvalidHTTPSchemeError struct { + // Scheme is the rejected scheme value (e.g. "mutual", "oauth"). + Scheme string + // Origin is the source location of the offending security scheme + // when the document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *InvalidHTTPSchemeError) Error() string { + return fmt.Sprintf("security scheme of type 'http' has invalid 'scheme' value %q", e.Scheme) +} + +// UnresolvedRefError clusters "found unresolved ref: X" failures fired +// by the loader when a $ref cannot be resolved against the loaded +// document. Carries the offending ref string so callers can surface +// it in user-facing output and filter findings by it. +type UnresolvedRefError struct { + // Ref is the unresolved $ref value (e.g. "#/components/schemas/X" + // or "external.yaml#/..."). + Ref string + // Origin is the source location of the ref-bearing object when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *UnresolvedRefError) Error() string { + return fmt.Sprintf("found unresolved ref: %q", e.Ref) +} + +// APIKeyInInvalidError clusters "apiKey should have 'in'. It can be +// 'query', 'header' or 'cookie', not X" failures. Fires when an +// apiKey security scheme either omits `in:` or sets it to a value +// outside {query, header, cookie}. Carries the rejected value so +// callers can render or filter; empty string means the field was +// missing entirely. +type APIKeyInInvalidError struct { + // Value is the rejected `in:` value (empty when the field was + // omitted, otherwise the bad value e.g. "body"). + Value string + // Origin is the source location of the offending security scheme + // when the document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *APIKeyInInvalidError) Error() string { + return fmt.Sprintf("security scheme of type 'apiKey' should have 'in'. It can be 'query', 'header' or 'cookie', not %q", e.Value) +} + +// PathMustStartWithSlashError clusters "path X does not start with a +// forward slash" failures. Path keys in the paths object must begin +// with `/`. +type PathMustStartWithSlashError struct { + // Path is the offending path key (e.g. "users/{id}"). + Path string + // Origin is the source location of the paths object when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *PathMustStartWithSlashError) Error() string { + return fmt.Sprintf("path %q does not start with a forward slash (/)", e.Path) +} + +// ConflictingPathsError clusters "conflicting paths X and Y" failures. +// Fires when two path keys normalize to the same template (e.g. +// "/users/{a}" and "/users/{b}" both normalize to "/users/{}"). +type ConflictingPathsError struct { + // Path1 / Path2 are the two conflicting path keys, in document + // order. + Path1 string + Path2 string + // Origin is the source location of the paths object when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *ConflictingPathsError) Error() string { + return fmt.Sprintf("conflicting paths %q and %q", e.Path1, e.Path2) +} + +// DuplicateParameterError clusters "more than one X parameter has +// name Y" failures. Fires when two parameters on an operation (or +// path item) share the same In + Name combination. +type DuplicateParameterError struct { + // In is the parameter location (e.g. "query", "path", "header"). + In string + // Name is the duplicated parameter name. + Name string + // Origin is the source location of the offending parameter when + // the document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *DuplicateParameterError) Error() string { + return fmt.Sprintf("more than one %q parameter has name %q", e.In, e.Name) +} + +// DuplicateRequiredFieldError clusters "duplicate field in required" failures. +// The elements of a schema's `required` array MUST be unique (JSON Schema +// 2020-12 §6.5.3 for OpenAPI 3.1, draft-04 for OpenAPI 3.0). +type DuplicateRequiredFieldError struct { + // Field is the field name listed more than once in `required`. + Field string + // Origin is the source location of the offending schema when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *DuplicateRequiredFieldError) Error() string { + return fmt.Sprintf("duplicate field %q in required", e.Field) +} + +// DuplicateTagError clusters "more than one tag has name X" failures. Each tag +// name in the document-root `tags` list MUST be unique (OpenAPI Object). +type DuplicateTagError struct { + // Name is the tag name that appears more than once. + Name string + // Origin is the source location of the offending tag when the document + // was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *DuplicateTagError) Error() string { + return fmt.Sprintf("more than one tag has name %q", e.Name) +} + +// InvalidSerializationMethodError clusters "serialization method with +// style=X and explode=Y is not supported by Z" failures. Fires for +// invalid (style, explode) combinations on encodings, parameters, +// and headers. The Subject discriminates which surface is reporting: +// "media type" for encoding, the parameter location ("path", +// "query", etc.) for parameters and "header" for headers. +type InvalidSerializationMethodError struct { + // Subject discriminates the calling surface ("media type", + // "path"/"query"/"header"/"cookie" for parameters, or "header" + // for the header.go site). + Subject string + // Style is the offending `style:` value. + Style string + // Explode is the offending `explode:` value. + Explode bool + // Origin is the source location of the offending object when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *InvalidSerializationMethodError) Error() string { + if e.Subject == "media type" { + return fmt.Sprintf("serialization method with style=%q and explode=%v is not supported by media type", e.Style, e.Explode) + } + return fmt.Sprintf("serialization method with style=%q and explode=%v is not supported by a %s parameter", e.Style, e.Explode, e.Subject) +} + +// --------------------------------------------------------------------- +// Leaf types — one per call site. Each embeds ValidationError for +// Error() and As-to-base, and is wrapped in its cluster type when +// returned from a validator. +// +// Naming convention: for required fields, +// FieldFor31Plus for 3.1-only fields used in 3.0 docs. +// Subjects use Go-identifier-friendly transliterations of OAS field +// paths ("$defs" -> "Defs", "$dynamicAnchor" -> "DynamicAnchor"). +// --------------------------------------------------------------------- + +// RequiredFieldError leaves. + +type InfoVersionRequired struct{ ValidationError } + +func (e *InfoVersionRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type InfoTitleRequired struct{ ValidationError } + +func (e *InfoTitleRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type LicenseNameRequired struct{ ValidationError } + +func (e *LicenseNameRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type OpenAPIVersionRequired struct{ ValidationError } + +func (e *OpenAPIVersionRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ServerURLRequired struct{ ValidationError } + +func (e *ServerURLRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// ServerURLTemplateError leaves. + +type ServerURLMismatchedBraces struct{ ValidationError } + +func (e *ServerURLMismatchedBraces) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ServerURLUndeclaredVariables struct{ ValidationError } + +func (e *ServerURLUndeclaredVariables) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SchemaItemsRequired struct{ ValidationError } + +func (e *SchemaItemsRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// EitherFieldRequiredError leaves. + +type ExampleValueOrExternalValueRequired struct{ ValidationError } + +func (e *ExampleValueOrExternalValueRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type LinkOperationIDOrRefRequired struct{ ValidationError } + +func (e *LinkOperationIDOrRefRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type InfoRequired struct{ ValidationError } + +func (e *InfoRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type PathsRequired struct{ ValidationError } + +func (e *PathsRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type JSONSchemaDialectAbsoluteURIRequired struct{ ValidationError } + +func (e *JSONSchemaDialectAbsoluteURIRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// SchemaBothFormsExclusive leaves. + +type SchemaAdditionalPropertiesBothForms struct{ ValidationError } + +func (e *SchemaAdditionalPropertiesBothForms) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SchemaUnevaluatedItemsBothForms struct{ ValidationError } + +func (e *SchemaUnevaluatedItemsBothForms) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SchemaUnevaluatedPropertiesBothForms struct{ ValidationError } + +func (e *SchemaUnevaluatedPropertiesBothForms) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// ExactlyOneFieldError leaves. + +type ParameterContentSchemaExactlyOne struct{ ValidationError } + +func (e *ParameterContentSchemaExactlyOne) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type HeaderContentSchemaExactlyOne struct{ ValidationError } + +func (e *HeaderContentSchemaExactlyOne) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// SingleEntryContentError leaves. + +type ParameterContentSingleEntry struct{ ValidationError } + +func (e *ParameterContentSingleEntry) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type HeaderContentSingleEntry struct{ ValidationError } + +func (e *HeaderContentSingleEntry) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// WebhookNilError leaf. + +type WebhookNil struct{ ValidationError } + +func (e *WebhookNil) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// SecurityScheme leaves wrapped in RequiredFieldError / ForbiddenFieldError. + +type OpenIDConnectURLRequired struct{ ValidationError } + +func (e *OpenIDConnectURLRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SecuritySchemeFlowsRequired struct{ ValidationError } + +func (e *SecuritySchemeFlowsRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SecuritySchemeInForbidden struct{ ValidationError } + +func (e *SecuritySchemeInForbidden) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SecuritySchemeNameForbidden struct{ ValidationError } + +func (e *SecuritySchemeNameForbidden) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SecuritySchemeBearerFormatForbidden struct{ ValidationError } + +func (e *SecuritySchemeBearerFormatForbidden) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SecuritySchemeFlowsForbidden struct{ ValidationError } + +func (e *SecuritySchemeFlowsForbidden) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ParameterExampleAndExamplesExclusive struct{ ValidationError } + +func (e *ParameterExampleAndExamplesExclusive) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ServerVariableDefaultRequired struct{ ValidationError } + +func (e *ServerVariableDefaultRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ExternalDocsURLRequired struct{ ValidationError } + +func (e *ExternalDocsURLRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type OperationResponsesRequired struct{ ValidationError } + +func (e *OperationResponsesRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type RequestBodyContentRequired struct{ ValidationError } + +func (e *RequestBodyContentRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ResponseDescriptionRequired struct{ ValidationError } + +func (e *ResponseDescriptionRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type OAuthFlowScopesRequired struct{ ValidationError } + +func (e *OAuthFlowScopesRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type OAuthFlowAuthorizationURLRequired struct{ ValidationError } + +func (e *OAuthFlowAuthorizationURLRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type OAuthFlowTokenURLRequired struct{ ValidationError } + +func (e *OAuthFlowTokenURLRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ParameterNameRequired struct{ ValidationError } + +func (e *ParameterNameRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ResponsesNonEmptyRequired struct{ ValidationError } + +func (e *ResponsesNonEmptyRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type APIKeySecuritySchemeNameRequired struct{ ValidationError } + +func (e *APIKeySecuritySchemeNameRequired) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// MutuallyExclusiveFieldsError leaves. + +type ExampleValueExternalValueExclusive struct{ ValidationError } + +func (e *ExampleValueExternalValueExclusive) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type MediaTypeExampleExamplesExclusive struct{ ValidationError } + +func (e *MediaTypeExampleExamplesExclusive) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type LicenseURLIdentifierExclusive struct{ ValidationError } + +func (e *LicenseURLIdentifierExclusive) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type LinkOperationIDRefExclusive struct{ ValidationError } + +func (e *LinkOperationIDRefExclusive) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SchemaReadOnlyWriteOnlyExclusive struct{ ValidationError } + +func (e *SchemaReadOnlyWriteOnlyExclusive) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// ForbiddenFieldError leaves. + +type HeaderNameForbidden struct{ ValidationError } + +func (e *HeaderNameForbidden) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type HeaderInForbidden struct{ ValidationError } + +func (e *HeaderInForbidden) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type OAuthFlowAuthorizationURLForbidden struct{ ValidationError } + +func (e *OAuthFlowAuthorizationURLForbidden) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type OAuthFlowTokenURLForbidden struct{ ValidationError } + +func (e *OAuthFlowTokenURLForbidden) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// FieldVersionMismatchError leaves — non-schema fields. + +type InfoSummaryFieldFor31Plus struct{ ValidationError } + +func (e *InfoSummaryFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type LicenseIdentifierFieldFor31Plus struct{ ValidationError } + +func (e *LicenseIdentifierFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type WebhooksFieldFor31Plus struct{ ValidationError } + +func (e *WebhooksFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type JSONSchemaDialectFieldFor31Plus struct{ ValidationError } + +func (e *JSONSchemaDialectFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// FieldVersionMismatchError leaves — schema fields (rejected by +// schema.go's reject() helper when a 3.0 doc uses 3.1 keywords). + +type ConstFieldFor31Plus struct{ ValidationError } + +func (e *ConstFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ExamplesFieldFor31Plus struct{ ValidationError } + +func (e *ExamplesFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type PrefixItemsFieldFor31Plus struct{ ValidationError } + +func (e *PrefixItemsFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ContainsFieldFor31Plus struct{ ValidationError } + +func (e *ContainsFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type MinContainsFieldFor31Plus struct{ ValidationError } + +func (e *MinContainsFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type MaxContainsFieldFor31Plus struct{ ValidationError } + +func (e *MaxContainsFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type PatternPropertiesFieldFor31Plus struct{ ValidationError } + +func (e *PatternPropertiesFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type DependentSchemasFieldFor31Plus struct{ ValidationError } + +func (e *DependentSchemasFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type PropertyNamesFieldFor31Plus struct{ ValidationError } + +func (e *PropertyNamesFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type UnevaluatedItemsFieldFor31Plus struct{ ValidationError } + +func (e *UnevaluatedItemsFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type UnevaluatedPropertiesFieldFor31Plus struct{ ValidationError } + +func (e *UnevaluatedPropertiesFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type IfFieldFor31Plus struct{ ValidationError } + +func (e *IfFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ThenFieldFor31Plus struct{ ValidationError } + +func (e *ThenFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ElseFieldFor31Plus struct{ ValidationError } + +func (e *ElseFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type DependentRequiredFieldFor31Plus struct{ ValidationError } + +func (e *DependentRequiredFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ContentEncodingFieldFor31Plus struct{ ValidationError } + +func (e *ContentEncodingFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ContentMediaTypeFieldFor31Plus struct{ ValidationError } + +func (e *ContentMediaTypeFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type ContentSchemaFieldFor31Plus struct{ ValidationError } + +func (e *ContentSchemaFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type DefsFieldFor31Plus struct{ ValidationError } + +func (e *DefsFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type SchemaFieldFor31Plus struct{ ValidationError } + +func (e *SchemaFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type CommentFieldFor31Plus struct{ ValidationError } + +func (e *CommentFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type IDFieldFor31Plus struct{ ValidationError } + +func (e *IDFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type AnchorFieldFor31Plus struct{ ValidationError } + +func (e *AnchorFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type DynamicAnchorFieldFor31Plus struct{ ValidationError } + +func (e *DynamicAnchorFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +type DynamicRefFieldFor31Plus struct{ ValidationError } + +func (e *DynamicRefFieldFor31Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +// --------------------------------------------------------------------- +// Constructors — the validator-side entry points. Build the leaf, wrap +// in the cluster, return the cluster (which exposes the leaf via +// Unwrap and the base via the leaf's As method). +// --------------------------------------------------------------------- + +func newRequiredField(field string, leaf error, origin *Origin) error { + return &RequiredFieldError{Field: field, Cause: leaf, Origin: origin} +} + +func newInfoVersionRequired(origin *Origin) error { + return newRequiredField("info.version", + &InfoVersionRequired{ValidationError{Message: "value of version must be a non-empty string"}}, origin) +} + +func newInfoTitleRequired(origin *Origin) error { + return newRequiredField("info.title", + &InfoTitleRequired{ValidationError{Message: "value of title must be a non-empty string"}}, origin) +} + +func newLicenseNameRequired(origin *Origin) error { + return newRequiredField("license.name", + &LicenseNameRequired{ValidationError{Message: "value of license name must be a non-empty string"}}, origin) +} + +func newOpenAPIVersionRequired(origin *Origin) error { + return newRequiredField("openapi", + &OpenAPIVersionRequired{ValidationError{Message: "value of openapi must be a non-empty string"}}, origin) +} + +func newServerURLRequired(origin *Origin) error { + return newRequiredField("server.url", + &ServerURLRequired{ValidationError{Message: "value of url must be a non-empty string"}}, origin) +} + +// newServerURLTemplateError wraps leaf in a *ServerURLTemplateError +// carrying the offending Server.URL. +func newServerURLTemplateError(serverURL string, leaf error, origin *Origin) error { + return &ServerURLTemplateError{URL: serverURL, Cause: leaf, Origin: origin} +} + +func newServerURLMismatchedBraces(serverURL string, origin *Origin) error { + const msg = "server URL has mismatched { and }" + return newServerURLTemplateError(serverURL, + &ServerURLMismatchedBraces{ValidationError{Message: msg}}, origin) +} + +func newServerURLUndeclaredVariables(serverURL string, origin *Origin) error { + const msg = "server has undeclared variables" + return newServerURLTemplateError(serverURL, + &ServerURLUndeclaredVariables{ValidationError{Message: msg}}, origin) +} + +func newSchemaItemsRequired(origin *Origin) error { + const msg = "when schema type is 'array', schema 'items' must be non-null" + return newRequiredField("schema.items", + &SchemaItemsRequired{ValidationError{Message: msg}}, origin) +} + +// newEitherFieldRequired wraps leaf in an *EitherFieldRequiredError +// carrying the set of field names, at least one of which must be set. +func newEitherFieldRequired(fields []string, leaf error, origin *Origin) error { + return &EitherFieldRequiredError{Fields: fields, Cause: leaf, Origin: origin} +} + +func newExampleValueOrExternalValueRequired(origin *Origin) error { + const msg = "no value or externalValue field" + return newEitherFieldRequired([]string{"value", "externalValue"}, + &ExampleValueOrExternalValueRequired{ValidationError{Message: msg}}, origin) +} + +func newLinkOperationIDOrRefRequired(origin *Origin) error { + const msg = "missing operationId or operationRef on link" + return newEitherFieldRequired([]string{"operationId", "operationRef"}, + &LinkOperationIDOrRefRequired{ValidationError{Message: msg}}, origin) +} + +func newInfoRequired(origin *Origin) error { + return newRequiredField("info", + &InfoRequired{ValidationError{Message: "must be an object"}}, origin) +} + +func newPathsRequired(origin *Origin) error { + return newRequiredField("paths", + &PathsRequired{ValidationError{Message: "must be an object"}}, origin) +} + +func newJSONSchemaDialectAbsoluteURIRequired(origin *Origin) error { + return newRequiredField("jsonSchemaDialect", + &JSONSchemaDialectAbsoluteURIRequired{ValidationError{Message: "must be an absolute URI with a scheme"}}, origin) +} + +// newSchemaBothForms wraps leaf in a *SchemaBothFormsExclusive carrying +// the name of the union-typed schema property. +func newSchemaBothForms(field string, leaf error, origin *Origin) error { + return &SchemaBothFormsExclusive{Field: field, Cause: leaf, Origin: origin} +} + +func newSchemaAdditionalPropertiesBothForms(origin *Origin) error { + const msg = "additionalProperties are set to both boolean and schema" + return newSchemaBothForms("additionalProperties", + &SchemaAdditionalPropertiesBothForms{ValidationError{Message: msg}}, origin) +} + +func newSchemaUnevaluatedItemsBothForms(origin *Origin) error { + const msg = "unevaluatedItems is set to both boolean and schema" + return newSchemaBothForms("unevaluatedItems", + &SchemaUnevaluatedItemsBothForms{ValidationError{Message: msg}}, origin) +} + +func newSchemaUnevaluatedPropertiesBothForms(origin *Origin) error { + const msg = "unevaluatedProperties is set to both boolean and schema" + return newSchemaBothForms("unevaluatedProperties", + &SchemaUnevaluatedPropertiesBothForms{ValidationError{Message: msg}}, origin) +} + +// newExactlyOneField wraps leaf in an *ExactlyOneFieldError carrying +// the set of fields, exactly one of which must be set. +func newExactlyOneField(fields []string, leaf error, origin *Origin) error { + return &ExactlyOneFieldError{Fields: fields, Cause: leaf, Origin: origin} +} + +func newParameterContentSchemaExactlyOne(origin *Origin) error { + const msg = "parameter must contain exactly one of content and schema" + return newExactlyOneField([]string{"content", "schema"}, + &ParameterContentSchemaExactlyOne{ValidationError{Message: msg}}, origin) +} + +// newHeaderContentSchemaExactlyOne formats the same way the existing +// header.go site does, including the historical "%v" dump of the +// Header struct, so the Error() string remains byte-identical. +func newHeaderContentSchemaExactlyOne(header any, origin *Origin) error { + msg := fmt.Sprintf("parameter must contain exactly one of content and schema: %v", header) + return newExactlyOneField([]string{"content", "schema"}, + &HeaderContentSchemaExactlyOne{ValidationError{Message: msg}}, origin) +} + +// newSingleEntryContent wraps leaf in a *SingleEntryContentError +// carrying the Subject ("parameter" or "header") whose Content map +// has more than one entry. +func newSingleEntryContent(subject string, leaf error, origin *Origin) error { + return &SingleEntryContentError{Subject: subject, Cause: leaf, Origin: origin} +} + +func newParameterContentSingleEntry(origin *Origin) error { + const msg = "parameter content must only contain one entry" + return newSingleEntryContent("parameter", + &ParameterContentSingleEntry{ValidationError{Message: msg}}, origin) +} + +func newHeaderContentSingleEntry(origin *Origin) error { + const msg = "parameter content must only contain one entry" + return newSingleEntryContent("header", + &HeaderContentSingleEntry{ValidationError{Message: msg}}, origin) +} + +func newWebhookNil(name string) error { + msg := fmt.Sprintf("webhook %q is nil", name) + return &WebhookNilError{ + Name: name, + Cause: &WebhookNil{ValidationError{Message: msg}}, + } +} + +func newExternalDocsURLRequired(origin *Origin) error { + return newRequiredField("externalDocs.url", + &ExternalDocsURLRequired{ValidationError{Message: "url is required"}}, origin) +} + +func newOperationResponsesRequired(origin *Origin) error { + return newRequiredField("operation.responses", + &OperationResponsesRequired{ValidationError{Message: "value of responses must be an object"}}, origin) +} + +func newRequestBodyContentRequired(origin *Origin) error { + return newRequiredField("requestBody.content", + &RequestBodyContentRequired{ValidationError{Message: "content of the request body is required"}}, origin) +} + +func newResponseDescriptionRequired(origin *Origin) error { + return newRequiredField("response.description", + &ResponseDescriptionRequired{ValidationError{Message: "a short description of the response is required"}}, origin) +} + +func newOAuthFlowScopesRequired(origin *Origin) error { + return newRequiredField("oAuthFlow.scopes", + &OAuthFlowScopesRequired{ValidationError{Message: "field 'scopes' is missing"}}, origin) +} + +func newOAuthFlowAuthorizationURLRequired(origin *Origin) error { + return newRequiredField("oAuthFlow.authorizationUrl", + &OAuthFlowAuthorizationURLRequired{ValidationError{Message: "field 'authorizationUrl' is empty or missing"}}, origin) +} + +func newOAuthFlowTokenURLRequired(origin *Origin) error { + return newRequiredField("oAuthFlow.tokenUrl", + &OAuthFlowTokenURLRequired{ValidationError{Message: "field 'tokenUrl' is empty or missing"}}, origin) +} + +// newMutuallyExclusiveFields wraps leaf in a *MutuallyExclusiveFieldsError +// carrying the two field names the spec forbids setting together. +func newMutuallyExclusiveFields(field1, field2 string, leaf error, origin *Origin) error { + return &MutuallyExclusiveFieldsError{ + Field1: field1, + Field2: field2, + Cause: leaf, + Origin: origin, + } +} + +func newExampleValueExternalValueExclusive(origin *Origin) error { + const msg = "value and externalValue are mutually exclusive" + return newMutuallyExclusiveFields("value", "externalValue", + &ExampleValueExternalValueExclusive{ValidationError{Message: msg}}, origin) +} + +func newMediaTypeExampleExamplesExclusive(origin *Origin) error { + const msg = "example and examples are mutually exclusive" + return newMutuallyExclusiveFields("example", "examples", + &MediaTypeExampleExamplesExclusive{ValidationError{Message: msg}}, origin) +} + +func newLicenseURLIdentifierExclusive(origin *Origin) error { + const msg = "license must not specify both 'url' and 'identifier'" + return newMutuallyExclusiveFields("url", "identifier", + &LicenseURLIdentifierExclusive{ValidationError{Message: msg}}, origin) +} + +func newLinkOperationIDRefExclusive(operationID, operationRef string, origin *Origin) error { + msg := fmt.Sprintf("operationId %q and operationRef %q are mutually exclusive", operationID, operationRef) + return newMutuallyExclusiveFields("operationId", "operationRef", + &LinkOperationIDRefExclusive{ValidationError{Message: msg}}, origin) +} + +func newSchemaReadOnlyWriteOnlyExclusive(origin *Origin) error { + const msg = "a property MUST NOT be marked as both readOnly and writeOnly being true" + return newMutuallyExclusiveFields("readOnly", "writeOnly", + &SchemaReadOnlyWriteOnlyExclusive{ValidationError{Message: msg}}, origin) +} + +// newForbiddenField wraps leaf in a *ForbiddenFieldError carrying the +// name of the field that the spec forbids in the current context. +func newForbiddenField(field string, leaf error, origin *Origin) error { + return &ForbiddenFieldError{Field: field, Cause: leaf, Origin: origin} +} + +func newHeaderNameForbidden(origin *Origin) error { + const msg = "header 'name' MUST NOT be specified, it is given in the corresponding headers map" + return newForbiddenField("name", + &HeaderNameForbidden{ValidationError{Message: msg}}, origin) +} + +func newHeaderInForbidden(origin *Origin) error { + const msg = "header 'in' MUST NOT be specified, it is implicitly in header" + return newForbiddenField("in", + &HeaderInForbidden{ValidationError{Message: msg}}, origin) +} + +func newOAuthFlowAuthorizationURLForbidden(origin *Origin) error { + const msg = "field 'authorizationUrl' should not be set" + return newForbiddenField("authorizationUrl", + &OAuthFlowAuthorizationURLForbidden{ValidationError{Message: msg}}, origin) +} + +func newOAuthFlowTokenURLForbidden(origin *Origin) error { + const msg = "field 'tokenUrl' should not be set" + return newForbiddenField("tokenUrl", + &OAuthFlowTokenURLForbidden{ValidationError{Message: msg}}, origin) +} + +func newParameterNameRequired(origin *Origin) error { + return newRequiredField("parameter.name", + &ParameterNameRequired{ValidationError{Message: "parameter name can't be blank"}}, origin) +} + +func newResponsesNonEmptyRequired(origin *Origin) error { + const msg = "the responses object MUST contain at least one response code" + return newRequiredField("responses", + &ResponsesNonEmptyRequired{ValidationError{Message: msg}}, origin) +} + +func newAPIKeySecuritySchemeNameRequired(origin *Origin) error { + const msg = "security scheme of type 'apiKey' should have 'name'" + return newRequiredField("securityScheme.name", + &APIKeySecuritySchemeNameRequired{ValidationError{Message: msg}}, origin) +} + +// newSchemaValueError wraps the result of schema.VisitJSON in a +// *SchemaValueError cluster, identifying which schema sub-field +// (example, default, ...) carried the offending value. cause is +// either a *SchemaError or a MultiError of them. +func newSchemaValueError(valueKind string, cause error, origin *Origin) error { + return &SchemaValueError{ValueKind: valueKind, Cause: cause, Origin: origin} +} + +// exampleValueOrigin returns an Origin pinned to the example's `value:` +// field, used when wrapping a plural Examples entry's validation failure. +// Falls back to the example's struct origin, then the parent fallback +// origin (parameter or media type), so consumers always have something +// useful to deep-link to. +func exampleValueOrigin(ex *Example, fallback *Origin) *Origin { + if ex == nil || ex.Origin == nil { + return fallback + } + if loc, ok := ex.Origin.Fields["value"]; ok { + return &Origin{Key: &loc} + } + return ex.Origin +} + +// newFieldVersionMismatch wraps leaf in a FieldVersionMismatchError for the +// given field at minimum version. Used by per-call-site constructors +// (newInfoSummaryFieldFor31Plus, etc.) and by dispatch helpers. +func newFieldVersionMismatch(field, minVersion string, leaf error, origin *Origin) error { + return &FieldVersionMismatchError{ + Field: field, + MinVersion: minVersion, + Cause: leaf, + Origin: origin, + } +} + +// Per-call-site constructors for the four non-schema FieldFor31Plus sites +// (info.summary, license.identifier, doc.webhooks, doc.jsonSchemaDialect). +// The schema fields go through fieldFor31PlusLeaves below because they're +// dispatched from a runtime-parameterised closure in schema.go's reject. + +func newInfoSummaryFieldFor31Plus(origin *Origin) error { + const msg = "field summary is for OpenAPI >=3.1" + return newFieldVersionMismatch("summary", + "3.1", &InfoSummaryFieldFor31Plus{ValidationError{Message: msg}}, origin) +} + +func newLicenseIdentifierFieldFor31Plus(origin *Origin) error { + const msg = "field identifier is for OpenAPI >=3.1" + return newFieldVersionMismatch("identifier", + "3.1", &LicenseIdentifierFieldFor31Plus{ValidationError{Message: msg}}, origin) +} + +func newWebhooksFieldFor31Plus(origin *Origin) error { + const msg = "field webhooks is for OpenAPI >=3.1" + return newFieldVersionMismatch("webhooks", + "3.1", &WebhooksFieldFor31Plus{ValidationError{Message: msg}}, origin) +} + +func newJSONSchemaDialectFieldFor31Plus(origin *Origin) error { + const msg = "field jsonschemadialect is for OpenAPI >=3.1" + return newFieldVersionMismatch("jsonschemadialect", + "3.1", &JSONSchemaDialectFieldFor31Plus{ValidationError{Message: msg}}, origin) +} + +// fieldFor31PlusLeaves maps field names (as passed to errFieldFor31Plus) +// to their typed leaf constructors. Only schema-keyword fields are in +// the table — those are dispatched at runtime from schema.go's reject +// closure. The four non-schema fields (summary, identifier, webhooks, +// jsonschemadialect) have direct constructors above. Any field not in +// the map falls back to a bare *ValidationError, so callers still get +// the cluster + base layers — only the per-leaf type is missing. +var fieldFor31PlusLeaves = map[string]func(msg string) error{ + "const": func(m string) error { return &ConstFieldFor31Plus{ValidationError{Message: m}} }, + "examples": func(m string) error { return &ExamplesFieldFor31Plus{ValidationError{Message: m}} }, + "prefixItems": func(m string) error { return &PrefixItemsFieldFor31Plus{ValidationError{Message: m}} }, + "contains": func(m string) error { return &ContainsFieldFor31Plus{ValidationError{Message: m}} }, + "minContains": func(m string) error { return &MinContainsFieldFor31Plus{ValidationError{Message: m}} }, + "maxContains": func(m string) error { return &MaxContainsFieldFor31Plus{ValidationError{Message: m}} }, + "patternProperties": func(m string) error { return &PatternPropertiesFieldFor31Plus{ValidationError{Message: m}} }, + "dependentSchemas": func(m string) error { return &DependentSchemasFieldFor31Plus{ValidationError{Message: m}} }, + "propertyNames": func(m string) error { return &PropertyNamesFieldFor31Plus{ValidationError{Message: m}} }, + "unevaluatedItems": func(m string) error { return &UnevaluatedItemsFieldFor31Plus{ValidationError{Message: m}} }, + "unevaluatedProperties": func(m string) error { return &UnevaluatedPropertiesFieldFor31Plus{ValidationError{Message: m}} }, + "if": func(m string) error { return &IfFieldFor31Plus{ValidationError{Message: m}} }, + "then": func(m string) error { return &ThenFieldFor31Plus{ValidationError{Message: m}} }, + "else": func(m string) error { return &ElseFieldFor31Plus{ValidationError{Message: m}} }, + "dependentRequired": func(m string) error { return &DependentRequiredFieldFor31Plus{ValidationError{Message: m}} }, + "contentEncoding": func(m string) error { return &ContentEncodingFieldFor31Plus{ValidationError{Message: m}} }, + "contentMediaType": func(m string) error { return &ContentMediaTypeFieldFor31Plus{ValidationError{Message: m}} }, + "contentSchema": func(m string) error { return &ContentSchemaFieldFor31Plus{ValidationError{Message: m}} }, + "$defs": func(m string) error { return &DefsFieldFor31Plus{ValidationError{Message: m}} }, + "$schema": func(m string) error { return &SchemaFieldFor31Plus{ValidationError{Message: m}} }, + "$comment": func(m string) error { return &CommentFieldFor31Plus{ValidationError{Message: m}} }, + "$id": func(m string) error { return &IDFieldFor31Plus{ValidationError{Message: m}} }, + "$anchor": func(m string) error { return &AnchorFieldFor31Plus{ValidationError{Message: m}} }, + "$dynamicAnchor": func(m string) error { return &DynamicAnchorFieldFor31Plus{ValidationError{Message: m}} }, + "$dynamicRef": func(m string) error { return &DynamicRefFieldFor31Plus{ValidationError{Message: m}} }, +} + +// errFieldFor31Plus dispatches errFieldFor31Plus's per-field message +// to the right typed leaf and wraps it in a FieldVersionMismatchError. +// Fields not in fieldFor31PlusLeaves fall back to a bare +// *ValidationError so the caller still gets a stable Message and the +// cluster + base layers; only the per-leaf type is missing. +// +// Reached only from schema.go's reject closure with a runtime field +// name; the four non-schema sites use direct constructors instead. +func errFieldFor31Plus(field string, origin *Origin) error { + msg := "field " + field + " is for OpenAPI >=3.1" + var leaf error + if ctor, ok := fieldFor31PlusLeaves[field]; ok { + leaf = ctor(msg) + } else { + leaf = &ValidationError{Message: msg} + } + return newFieldVersionMismatch(field, "3.1", leaf, origin) +} + +func errFieldFor32Plus(field string, origin *Origin) error { + msg := "field " + field + " is for OpenAPI >=3.2" + return newFieldVersionMismatch(field, "3.2", &ValidationError{Message: msg}, origin) +} + +func newPathParameterRequired(param string, origin *Origin) error { + return &PathParameterRequiredError{Param: param, Origin: origin} +} + +func newDuplicateOperationID(endpoint1, endpoint2, operationID string, origin *Origin) error { + return &DuplicateOperationIDError{ + Endpoint1: endpoint1, + Endpoint2: endpoint2, + OperationID: operationID, + Origin: origin, + } +} + +func newExtraSiblingFields(fields []string, origin *Origin) error { + return &ExtraSiblingFieldsError{Fields: fields, Origin: origin} +} + +func newSchemaTypeError(typ string, origin *Origin) error { + return &SchemaTypeError{Type: typ, Origin: origin} +} + +func newInvalidParameterIn(value string, origin *Origin) error { + return &InvalidParameterInError{Value: value, Origin: origin} +} + +func newSchemaPatternRegexError(pattern string, cause error, origin *Origin) error { + return &SchemaPatternRegexError{Pattern: pattern, Cause: cause, Origin: origin} +} + +func newInvalidSecuritySchemeType(typ string, origin *Origin) error { + return &InvalidSecuritySchemeTypeError{Type: typ, Origin: origin} +} + +func newInvalidHTTPScheme(scheme string, origin *Origin) error { + return &InvalidHTTPSchemeError{Scheme: scheme, Origin: origin} +} + +func newUnresolvedRef(ref string, origin *Origin) error { + return &UnresolvedRefError{Ref: ref, Origin: origin} +} + +func newAPIKeyInInvalid(value string, origin *Origin) error { + return &APIKeyInInvalidError{Value: value, Origin: origin} +} + +func newOpenIDConnectURLRequired(schemeName string, origin *Origin) error { + return newRequiredField("openIdConnectUrl", + &OpenIDConnectURLRequired{ValidationError{Message: fmt.Sprintf("no OIDC URL found for openIdConnect security scheme %q", schemeName)}}, origin) +} + +func newSecuritySchemeFlowsRequired(schemeType string, origin *Origin) error { + return newRequiredField("flows", + &SecuritySchemeFlowsRequired{ValidationError{Message: fmt.Sprintf("security scheme of type %q should have 'flows'", schemeType)}}, origin) +} + +func newSecuritySchemeInForbidden(schemeType string, origin *Origin) error { + return newForbiddenField("in", + &SecuritySchemeInForbidden{ValidationError{Message: fmt.Sprintf("security scheme of type %q can't have 'in'", schemeType)}}, origin) +} + +func newSecuritySchemeNameForbidden(schemeType string, origin *Origin) error { + return newForbiddenField("name", + &SecuritySchemeNameForbidden{ValidationError{Message: fmt.Sprintf("security scheme of type %q can't have 'name'", schemeType)}}, origin) +} + +func newSecuritySchemeBearerFormatForbidden(schemeType string, origin *Origin) error { + return newForbiddenField("bearerFormat", + &SecuritySchemeBearerFormatForbidden{ValidationError{Message: fmt.Sprintf("security scheme of type %q can't have 'bearerFormat'", schemeType)}}, origin) +} + +func newSecuritySchemeFlowsForbidden(schemeType string, origin *Origin) error { + return newForbiddenField("flows", + &SecuritySchemeFlowsForbidden{ValidationError{Message: fmt.Sprintf("security scheme of type %q can't have 'flows'", schemeType)}}, origin) +} + +func newPathMustStartWithSlash(path string, origin *Origin) error { + return &PathMustStartWithSlashError{Path: path, Origin: origin} +} + +func newConflictingPaths(path1, path2 string, origin *Origin) error { + return &ConflictingPathsError{Path1: path1, Path2: path2, Origin: origin} +} + +func newDuplicateParameter(in, name string, origin *Origin) error { + return &DuplicateParameterError{In: in, Name: name, Origin: origin} +} + +func newInvalidSerializationMethod(subject, style string, explode bool, origin *Origin) error { + return &InvalidSerializationMethodError{Subject: subject, Style: style, Explode: explode, Origin: origin} +} + +func newParameterExampleAndExamplesExclusive(parameterName string, origin *Origin) error { + return newMutuallyExclusiveFields("example", "examples", + &ParameterExampleAndExamplesExclusive{ValidationError{Message: fmt.Sprintf("parameter %q example and examples are mutually exclusive", parameterName)}}, origin) +} + +func newServerVariableDefaultRequired(serverData string, origin *Origin) error { + return newRequiredField("default", + &ServerVariableDefaultRequired{ValidationError{Message: fmt.Sprintf("field default is required in %s", serverData)}}, origin) +} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/validation_error_context.go b/vendor/github.com/getkin/kin-openapi/openapi3/validation_error_context.go new file mode 100644 index 000000000..50da3dceb --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/validation_error_context.go @@ -0,0 +1,300 @@ +// Context wrappers are the 4th category of typed validation errors, +// alongside the Base / Cluster / Leaf model documented at the top of +// validation_error.go. +// +// A context wrapper carries scope (which section, path, operation, +// component, parameter, OAuth flow, ...) around an inner error chain. +// It does NOT itself report a validation failure — the actual error +// lives in Cause and is reachable via errors.Unwrap / errors.As. +// +// Use a context wrapper to extract "where the error happened" without +// parsing the rendered message. Combine with errors.As against the +// inner cluster or leaf to also extract "what category" and "exactly +// which case." A canonical error chain looks like: +// +// ComponentValidationError{Section, Name} // context wrapper: WHERE +// -> RequiredFieldError{Field} // cluster: WHAT CATEGORY +// -> SomeFieldRequired{Message} // leaf: EXACTLY WHICH CASE +// +// Two scopes of context wrapper live in this file: +// +// - Document-wide wrappers — SectionValidationError, +// PathValidationError, OperationValidationError. Cover entire +// top-level scopes of the document. +// - Narrow-scope wrappers — ComponentValidationError, +// ExternalDocsURLValidationError, HeaderFieldValidationError, +// MediaTypeExampleValidationError, WebhookValidationError, +// ParameterFieldValidationError, ParameterExampleValidationError, +// SecuritySchemeFlowValidationError, OAuthFlowValidationError, +// OAuthFlowFieldValidationError, SchemaCombinatorElementValidationError, +// TagValidationError. Cover a specific validation surface inside a +// section. +// +// Both scopes follow the same shape: +// +// - One or more discriminator fields naming the scope (Section, +// Path, ParameterName + Field, etc.). +// - A Cause error that holds the wrapped inner error. +// - Unwrap() returns Cause so errors.As walks transparently to the +// inner cluster or leaf. +// +// Error() formats vary per wrapper: each preserves the original +// fmt.Errorf-with-%w message format byte-for-byte for backward +// compatibility, so existing string-matching consumers see identical +// output. There is no canonical ": " format across +// wrappers — read each type's Error() if the exact string matters. + +package openapi3 + +import "fmt" + +// SectionValidationError wraps an error originating inside one of the +// top-level OpenAPI document sections (info, paths, components, +// security, servers, tags, externalDocs, webhooks, jsonSchemaDialect). +// Section is the OpenAPI field name as it appears in the document +// root. +// +// Use errors.As(err, &sve) to extract the section context from a +// validation error chain without parsing the rendered message. +type SectionValidationError struct { + Section string + Cause error +} + +func (e *SectionValidationError) Error() string { + return fmt.Sprintf("invalid %s: %v", e.Section, e.Cause) +} + +func (e *SectionValidationError) Unwrap() error { return e.Cause } + +// PathValidationError wraps an error originating inside a specific path. +// Path is the path template as it appears in the document (e.g. +// "/users/{id}"). +// +// Use errors.As(err, &pve) to extract the path from a validation +// error chain without parsing the rendered message. +type PathValidationError struct { + Path string + Cause error +} + +func (e *PathValidationError) Error() string { + return fmt.Sprintf("invalid path %s: %v", e.Path, e.Cause) +} + +func (e *PathValidationError) Unwrap() error { return e.Cause } + +// OperationValidationError wraps an error originating inside a specific +// HTTP-method operation under a path. Method is the uppercase method +// (GET, POST, etc.). +// +// Use errors.As(err, &ove) to extract the method from a validation +// error chain without parsing the rendered message. +type OperationValidationError struct { + Method string + Cause error +} + +func (e *OperationValidationError) Error() string { + return fmt.Sprintf("invalid operation %s: %v", e.Method, e.Cause) +} + +func (e *OperationValidationError) Unwrap() error { return e.Cause } + +// Below: narrow-scope context wrappers covering specific validation +// surfaces inside a section. See the file-level header above for the +// full inventory and how these relate to the document-wide wrappers +// above. + +// ComponentValidationError wraps validation errors inside the +// Components container, carrying which sub-section (Schemas, +// Parameters, etc.) and which component name failed. +type ComponentValidationError struct { + // Section is the lowercase singular form of the component bucket + // ("schema", "parameter", "header", "request body", "response", + // "security scheme", "example", "link", "callback"). + Section string + // Name is the component map key. + Name string + Cause error +} + +func (e *ComponentValidationError) Error() string { + return fmt.Sprintf("%s %q: %v", e.Section, e.Name, e.Cause) +} + +func (e *ComponentValidationError) Unwrap() error { return e.Cause } + +// ExternalDocsURLValidationError wraps the URL parse failure on an +// ExternalDocs object. +type ExternalDocsURLValidationError struct { + Cause error +} + +func (e *ExternalDocsURLValidationError) Error() string { + return fmt.Sprintf("url is incorrect: %v", e.Cause) +} + +func (e *ExternalDocsURLValidationError) Unwrap() error { return e.Cause } + +// HeaderFieldValidationError wraps validation errors on a Header's +// `schema` or `content` sub-objects. Field discriminates the two. +type HeaderFieldValidationError struct { + // Field is "schema" or "content". + Field string + Cause error +} + +func (e *HeaderFieldValidationError) Error() string { + return fmt.Sprintf("header %s is invalid: %v", e.Field, e.Cause) +} + +func (e *HeaderFieldValidationError) Unwrap() error { return e.Cause } + +// MediaTypeExampleValidationError wraps validation errors on a named +// example inside a MediaType.examples map. +type MediaTypeExampleValidationError struct { + // ExampleName is the example map key. + ExampleName string + Cause error +} + +func (e *MediaTypeExampleValidationError) Error() string { + return fmt.Sprintf("example %s: %v", e.ExampleName, e.Cause) +} + +func (e *MediaTypeExampleValidationError) Unwrap() error { return e.Cause } + +// WebhookValidationError wraps validation errors on a named webhook +// at the document root (OpenAPI 3.1+). +type WebhookValidationError struct { + // Name is the webhook map key. + Name string + Cause error +} + +func (e *WebhookValidationError) Error() string { + return fmt.Sprintf("webhook %q: %v", e.Name, e.Cause) +} + +func (e *WebhookValidationError) Unwrap() error { return e.Cause } + +// ParameterFieldValidationError wraps validation errors on a +// parameter's `schema` or `content` sub-objects. Field discriminates. +type ParameterFieldValidationError struct { + // ParameterName is the parameter's `name:` value. + ParameterName string + // Field is "schema" or "content". + Field string + Cause error +} + +func (e *ParameterFieldValidationError) Error() string { + return fmt.Sprintf("parameter %q %s is invalid: %v", e.ParameterName, e.Field, e.Cause) +} + +func (e *ParameterFieldValidationError) Unwrap() error { return e.Cause } + +// ParameterExampleValidationError wraps validation errors on a named +// example inside a parameter's examples map. +type ParameterExampleValidationError struct { + // ExampleName is the example map key. + ExampleName string + Cause error +} + +func (e *ParameterExampleValidationError) Error() string { + return fmt.Sprintf("%s: %v", e.ExampleName, e.Cause) +} + +func (e *ParameterExampleValidationError) Unwrap() error { return e.Cause } + +// SecuritySchemeFlowValidationError wraps validation errors on the +// outer flows object of an oauth2 security scheme. +type SecuritySchemeFlowValidationError struct { + Cause error +} + +func (e *SecuritySchemeFlowValidationError) Error() string { + return fmt.Sprintf("security scheme 'flow' is invalid: %v", e.Cause) +} + +func (e *SecuritySchemeFlowValidationError) Unwrap() error { return e.Cause } + +// OAuthFlowValidationError wraps validation errors on a specific +// OAuth flow inside OAuthFlows. +type OAuthFlowValidationError struct { + // FlowKind is one of "implicit", "password", "clientCredentials", + // "authorizationCode". + FlowKind string + Cause error +} + +func (e *OAuthFlowValidationError) Error() string { + return fmt.Sprintf("the OAuth flow %q is invalid: %v", e.FlowKind, e.Cause) +} + +func (e *OAuthFlowValidationError) Unwrap() error { return e.Cause } + +// OAuthFlowFieldValidationError wraps validation errors on a specific +// field inside an OAuthFlow object. Field discriminates which URL +// field failed. +type OAuthFlowFieldValidationError struct { + // Field is the offending field name ("refreshUrl" is the only + // site today; future URL fields can reuse the same wrapper). + Field string + Cause error +} + +func (e *OAuthFlowFieldValidationError) Error() string { + return fmt.Sprintf("field %q is invalid: %v", e.Field, e.Cause) +} + +func (e *OAuthFlowFieldValidationError) Unwrap() error { return e.Cause } + +// SchemaCombinatorElementValidationError wraps a validation error +// originating in one of the sub-schemas listed under a schema's oneOf, +// anyOf, or allOf keyword. Combinator names which keyword the offending +// element belongs to ("oneOf", "anyOf", "allOf"). The wrapper adds the +// combinator scope; the actual failure lives in Cause. +type SchemaCombinatorElementValidationError struct { + // Combinator is the keyword whose element failed ("oneOf", "anyOf", + // "allOf"). + Combinator string + Cause error +} + +func (e *SchemaCombinatorElementValidationError) Error() string { + // Collapse a run of same-combinator wrappers so deeply nested + // allOf/anyOf/oneOf does not stutter "invalid allOf element: " once per + // nesting level. The typed chain is untouched (Unwrap and errors.As still + // see every level); only the rendered message drops the repeats. A run of + // a different combinator is preserved, so an allOf inside a oneOf still + // shows both scopes. + cause := e.Cause + for { + inner, ok := cause.(*SchemaCombinatorElementValidationError) + if !ok || inner.Combinator != e.Combinator { + break + } + cause = inner.Cause + } + return fmt.Sprintf("invalid %s element: %v", e.Combinator, cause) +} + +func (e *SchemaCombinatorElementValidationError) Unwrap() error { return e.Cause } + +// TagValidationError wraps a validation error originating inside a single +// tag in the document-root Tags list. Name is the tag's `name:` value, so +// callers can tell which tag failed without parsing the rendered message. +type TagValidationError struct { + // Name is the tag's `name:` value. + Name string + Cause error +} + +func (e *TagValidationError) Error() string { + return fmt.Sprintf("tag %q: %v", e.Name, e.Cause) +} + +func (e *TagValidationError) Unwrap() error { return e.Cause } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go b/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go index 1d141d40a..a7ad0f3e8 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go @@ -13,6 +13,10 @@ type ValidationOptions struct { schemaFormatValidationEnabled bool schemaPatternValidationDisabled bool schemaExtensionsInRefProhibited bool + jsonSchema2020ValidationEnabled bool + isOpenAPI31OrLater bool + isOpenAPI32OrLater bool + multiErrorEnabled bool regexCompilerFunc RegexCompilerFunc extraSiblingFieldsAllowed map[string]struct{} } @@ -31,6 +35,23 @@ func AllowExtraSiblingFields(fields ...string) ValidationOption { } } +// IsOpenAPI31OrLater enables "JSON Schema Draft 2020-12"-compliant validation (for OpenAPI 3.1 documents). +func IsOpenAPI31OrLater() ValidationOption { + return func(options *ValidationOptions) { + options.isOpenAPI31OrLater = true // To distinguish from v3.0 + options.jsonSchema2020ValidationEnabled = true // TODO: use even for v3.0 + } +} + +// IsOpenAPI32OrLater enables validation for OpenAPI 3.2 documents. +func IsOpenAPI32OrLater() ValidationOption { + return func(options *ValidationOptions) { + options.isOpenAPI31OrLater = true + options.isOpenAPI32OrLater = true + options.jsonSchema2020ValidationEnabled = true + } +} + // EnableSchemaFormatValidation makes Validate not return an error when validating documents that mention schema formats that are not defined by the OpenAPIv3 specification. // By default, schema format validation is disabled. func EnableSchemaFormatValidation() ValidationOption { @@ -114,6 +135,25 @@ func ProhibitExtensionsWithRef() ValidationOption { } } +// EnableMultiError makes Validate aggregate independent validation errors and +// return them together as a MultiError instead of returning the first error +// and stopping. By default, Validate is fail-fast. +// +// Not every validator reports more than one error yet. Some, such as Schema, +// run checks that build on earlier ones, so continuing past a failure can hit +// a nil dereference or produce nonsense secondary errors. +// We will keep converting more validators in follow-up changes as each one +// is analyzed. +// +// To pull a specific error type out of the result, use errors.As(err, &target). +// It walks into the MultiError automatically, so the same call works whether +// Validate returned one error or many. +func EnableMultiError() ValidationOption { + return func(options *ValidationOptions) { + options.multiErrorEnabled = true + } +} + // SetRegexCompiler allows to override the regex implementation used to validate // field "pattern". func SetRegexCompiler(c RegexCompilerFunc) ValidationOption { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/walk.go b/vendor/github.com/getkin/kin-openapi/openapi3/walk.go new file mode 100644 index 000000000..8d7b9563b --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/walk.go @@ -0,0 +1,318 @@ +package openapi3 + +import ( + "errors" + "maps" + "slices" + "strconv" + "strings" +) + +// SkipSubtree, when returned by a WalkSchemasFunc, tells WalkSchemas not to +// descend into the current schema's sub-schemas. The schema itself has already +// been visited. Any other non-nil error stops the walk and is returned by +// WalkSchemas. It mirrors filepath.SkipDir. +var SkipSubtree = errors.New("skip schema subtree") + +// WalkSchemasFunc is called once for each schema visited by WalkSchemas. +// +// jsonPointer is the RFC 6901 JSON Pointer of the schema within the document, +// e.g. "/components/schemas/Pet/properties/tag" or +// "/paths/~1pets/get/responses/200/content/application~1json/schema". It is +// file-agnostic: once the loader has resolved external $refs the document is a +// single tree, so the pointer addresses a position in that tree and never +// encodes a source file. schema is non-nil and schema.Value is non-nil; the +// callback may modify schema.Value in place, so WalkSchemas serves transformers +// and not only read-only inspection. +// +// Returning SkipSubtree skips this schema's sub-schemas; returning any other +// error aborts the walk and is returned by WalkSchemas. +type WalkSchemasFunc func(jsonPointer string, schema *SchemaRef) error + +// WalkSchemas visits every schema reachable from the document exactly once, +// invoking fn for each. It follows resolved $ref targets (schema.Value) and +// guards against reference cycles, so each distinct *Schema is visited a single +// time regardless of how many references point at it. Maps are visited in +// sorted key order, so the traversal is deterministic. +// +// It covers schemas under components (schemas, parameters, headers, request +// bodies, responses, callbacks), the paths and their operations (parameters, +// request bodies, responses, headers, callbacks), and webhooks, then recurses +// through every sub-schema keyword: properties, items, itemSchema, +// allOf/anyOf/oneOf, not, additionalProperties, prefixItems, contains, patternProperties, +// dependentSchemas, propertyNames, if/then/else, and $defs. +// +// It is useful for validation, code generation, schema transformation, +// $ref/dependency analysis, and documentation: any consumer that needs to act +// on every schema in a document without re-deriving the (easy to get wrong) +// traversal itself. +func (doc *T) WalkSchemas(fn WalkSchemasFunc) error { + if doc == nil { + return nil + } + w := schemaWalker{fn: fn, seen: make(map[*Schema]struct{})} + return w.document(doc) +} + +type schemaWalker struct { + fn WalkSchemasFunc + seen map[*Schema]struct{} +} + +// escapeRefString escapes a single JSON Pointer reference token per RFC 6901: +// '~' becomes '~0' and '/' becomes '~1'. It is the inverse of unescapeRefString. +func escapeRefString(s string) string { + if !strings.ContainsAny(s, "~/") { + return s + } + return strings.NewReplacer("~", "~0", "/", "~1").Replace(s) +} + +func (w *schemaWalker) document(doc *T) error { + if c := doc.Components; c != nil { + for _, name := range slices.Sorted(maps.Keys(c.Schemas)) { + if err := w.schemaRef("/components/schemas/"+escapeRefString(name), c.Schemas[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(c.Parameters)) { + if err := w.parameter("/components/parameters/"+escapeRefString(name), c.Parameters[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(c.Headers)) { + if err := w.header("/components/headers/"+escapeRefString(name), c.Headers[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(c.RequestBodies)) { + if rbr := c.RequestBodies[name]; rbr != nil && rbr.Value != nil { + if err := w.content("/components/requestBodies/"+escapeRefString(name)+"/content", rbr.Value.Content); err != nil { + return err + } + } + } + for _, name := range slices.Sorted(maps.Keys(c.Responses)) { + if err := w.response("/components/responses/"+escapeRefString(name), c.Responses[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(c.Callbacks)) { + if cbr := c.Callbacks[name]; cbr != nil && cbr.Value != nil { + if err := w.callback("/components/callbacks/"+escapeRefString(name), cbr.Value); err != nil { + return err + } + } + } + } + if doc.Paths != nil { + items := doc.Paths.Map() + for _, path := range slices.Sorted(maps.Keys(items)) { + if err := w.pathItem("/paths/"+escapeRefString(path), items[path]); err != nil { + return err + } + } + } + for _, name := range slices.Sorted(maps.Keys(doc.Webhooks)) { + if err := w.pathItem("/webhooks/"+escapeRefString(name), doc.Webhooks[name]); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) pathItem(ptr string, item *PathItem) error { + if item == nil { + return nil + } + for i, pr := range item.Parameters { + if err := w.parameter(ptr+"/parameters/"+strconv.Itoa(i), pr); err != nil { + return err + } + } + ops := item.Operations() + for _, method := range slices.Sorted(maps.Keys(ops)) { + if err := w.operation(ptr+"/"+strings.ToLower(method), ops[method]); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) operation(ptr string, op *Operation) error { + if op == nil { + return nil + } + for i, pr := range op.Parameters { + if err := w.parameter(ptr+"/parameters/"+strconv.Itoa(i), pr); err != nil { + return err + } + } + if op.RequestBody != nil && op.RequestBody.Value != nil { + if err := w.content(ptr+"/requestBody/content", op.RequestBody.Value.Content); err != nil { + return err + } + } + if op.Responses != nil { + responses := op.Responses.Map() + for _, code := range slices.Sorted(maps.Keys(responses)) { + if err := w.response(ptr+"/responses/"+escapeRefString(code), responses[code]); err != nil { + return err + } + } + } + for _, name := range slices.Sorted(maps.Keys(op.Callbacks)) { + if cbr := op.Callbacks[name]; cbr != nil && cbr.Value != nil { + if err := w.callback(ptr+"/callbacks/"+escapeRefString(name), cbr.Value); err != nil { + return err + } + } + } + return nil +} + +func (w *schemaWalker) callback(ptr string, cb *Callback) error { + items := cb.Map() + for _, expr := range slices.Sorted(maps.Keys(items)) { + if err := w.pathItem(ptr+"/"+escapeRefString(expr), items[expr]); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) parameter(ptr string, pr *ParameterRef) error { + if pr == nil || pr.Value == nil { + return nil + } + if err := w.schemaRef(ptr+"/schema", pr.Value.Schema); err != nil { + return err + } + return w.content(ptr+"/content", pr.Value.Content) +} + +func (w *schemaWalker) header(ptr string, hr *HeaderRef) error { + if hr == nil || hr.Value == nil { + return nil + } + if err := w.schemaRef(ptr+"/schema", hr.Value.Schema); err != nil { + return err + } + return w.content(ptr+"/content", hr.Value.Content) +} + +func (w *schemaWalker) response(ptr string, rr *ResponseRef) error { + if rr == nil || rr.Value == nil { + return nil + } + for _, name := range slices.Sorted(maps.Keys(rr.Value.Headers)) { + if err := w.header(ptr+"/headers/"+escapeRefString(name), rr.Value.Headers[name]); err != nil { + return err + } + } + return w.content(ptr+"/content", rr.Value.Content) +} + +func (w *schemaWalker) content(ptr string, content Content) error { + for _, mediaType := range slices.Sorted(maps.Keys(content)) { + media := content[mediaType] + if media == nil { + continue + } + if err := w.schemaRef(ptr+"/"+escapeRefString(mediaType)+"/schema", media.Schema); err != nil { + return err + } + if err := w.schemaRef(ptr+"/"+escapeRefString(mediaType)+"/itemSchema", media.ItemSchema); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) schemaRefs(ptr string, refs SchemaRefs) error { + for i, sub := range refs { + if err := w.schemaRef(ptr+"/"+strconv.Itoa(i), sub); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) schemaRef(ptr string, sr *SchemaRef) error { + if sr == nil || sr.Value == nil { + return nil + } + s := sr.Value + if _, ok := w.seen[s]; ok { + // Already visited (shared $ref target or reference cycle). + return nil + } + w.seen[s] = struct{}{} + + if err := w.fn(ptr, sr); err != nil { + if errors.Is(err, SkipSubtree) { + return nil + } + return err + } + + for _, name := range slices.Sorted(maps.Keys(s.Properties)) { + if err := w.schemaRef(ptr+"/properties/"+escapeRefString(name), s.Properties[name]); err != nil { + return err + } + } + if err := w.schemaRef(ptr+"/items", s.Items); err != nil { + return err + } + if s.AdditionalProperties.Schema != nil { + if err := w.schemaRef(ptr+"/additionalProperties", s.AdditionalProperties.Schema); err != nil { + return err + } + } + if err := w.schemaRefs(ptr+"/allOf", s.AllOf); err != nil { + return err + } + if err := w.schemaRefs(ptr+"/anyOf", s.AnyOf); err != nil { + return err + } + if err := w.schemaRefs(ptr+"/oneOf", s.OneOf); err != nil { + return err + } + if err := w.schemaRef(ptr+"/not", s.Not); err != nil { + return err + } + if err := w.schemaRefs(ptr+"/prefixItems", s.PrefixItems); err != nil { + return err + } + if err := w.schemaRef(ptr+"/contains", s.Contains); err != nil { + return err + } + for _, name := range slices.Sorted(maps.Keys(s.PatternProperties)) { + if err := w.schemaRef(ptr+"/patternProperties/"+escapeRefString(name), s.PatternProperties[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(s.DependentSchemas)) { + if err := w.schemaRef(ptr+"/dependentSchemas/"+escapeRefString(name), s.DependentSchemas[name]); err != nil { + return err + } + } + if err := w.schemaRef(ptr+"/propertyNames", s.PropertyNames); err != nil { + return err + } + if err := w.schemaRef(ptr+"/if", s.If); err != nil { + return err + } + if err := w.schemaRef(ptr+"/then", s.Then); err != nil { + return err + } + if err := w.schemaRef(ptr+"/else", s.Else); err != nil { + return err + } + for _, name := range slices.Sorted(maps.Keys(s.Defs)) { + if err := w.schemaRef(ptr+"/$defs/"+escapeRefString(name), s.Defs[name]); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/xml.go b/vendor/github.com/getkin/kin-openapi/openapi3/xml.go index e960249d3..508c871a6 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/xml.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/xml.go @@ -3,13 +3,14 @@ package openapi3 import ( "context" "encoding/json" + "maps" ) // XML is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#xml-object type XML struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` + Origin *Origin `json:"-" yaml:"-"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` @@ -30,9 +31,7 @@ func (xml XML) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of XML. func (xml XML) MarshalYAML() (any, error) { m := make(map[string]any, 5+len(xml.Extensions)) - for k, v := range xml.Extensions { - m[k] = v - } + maps.Copy(m, xml.Extensions) if x := xml.Name; x != "" { m["name"] = x } @@ -59,7 +58,6 @@ func (xml *XML) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) - delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "namespace") delete(x.Extensions, "prefix") @@ -76,5 +74,5 @@ func (xml *XML) UnmarshalJSON(data []byte) error { func (xml *XML) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) - return validateExtensions(ctx, xml.Extensions) + return validateExtensions(ctx, xml.Extensions, xml.Origin) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/errors.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/errors.go index ea7c7c312..1039bed51 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/errors.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/errors.go @@ -33,9 +33,8 @@ func (err *RequestError) Error() string { return fmt.Sprintf("parameter %q in %s has an error: %s", v.Name, v.In, reason) } else if v := err.RequestBody; v != nil { return fmt.Sprintf("request body has an error: %s", reason) - } else { - return reason } + return reason } func (err RequestError) Unwrap() error { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/internal.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/internal.go index f807e06f1..bb8ba6b79 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/internal.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/internal.go @@ -6,11 +6,11 @@ import ( ) func parseMediaType(contentType string) string { - i := strings.IndexByte(contentType, ';') - if i < 0 { + before, _, ok := strings.Cut(contentType, ";") + if !ok { return contentType } - return contentType[:i] + return before } func isNilValue(value any) bool { @@ -18,7 +18,7 @@ func isNilValue(value any) bool { return true } switch reflect.TypeOf(value).Kind() { - case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: + case reflect.Pointer, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: return reflect.ValueOf(value).IsNil() } return false diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/options.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/options.go index e7fad8321..b85f932b2 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/options.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/options.go @@ -28,6 +28,9 @@ type Options struct { // Set RegexCompiler to override the regex implementation RegexCompiler openapi3.RegexCompilerFunc + // Set RejectWhenRequestBodyNotSpecified so ValidateRequest fails when request body is present but not defined in the specification + RejectWhenRequestBodyNotSpecified bool + // A document with security schemes defined will not pass validation // unless an AuthenticationFunc is defined. // See NoopAuthenticationFunc @@ -38,6 +41,10 @@ type Options struct { SkipSettingDefaults bool customSchemaErrorFunc CustomSchemaErrorFunc + + // Additional schema validation options to pass through to schema validation. + // Use this to pass document-scoped format validators or other per-validation options. + SchemaValidationOptions []openapi3.SchemaValidationOption } // CustomSchemaErrorFunc allows for custom the schema error message. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_decoder.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_decoder.go index e5fe2a980..6e7efdeef 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_decoder.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_decoder.go @@ -8,16 +8,18 @@ import ( "errors" "fmt" "io" + "maps" "mime" "mime/multipart" "net/http" "net/url" "reflect" "regexp" + "slices" "strconv" "strings" - "github.com/oasdiff/yaml3" + yaml "github.com/oasdiff/yaml3" "github.com/getkin/kin-openapi/openapi3" ) @@ -539,14 +541,14 @@ func (d *urlValuesDecoder) DecodeArray(param string, sm *openapi3.SerializationM } values = strings.Split(values[0], delim) } - val, err := d.parseArray(values, sm, schema) + val, err := d.parseArray(values, schema) return val, ok, err } // parseArray returns an array that contains items from a raw array. // Every item is parsed as a primitive value. // The function returns an error when an error happened while parse array's items. -func (d *urlValuesDecoder) parseArray(raw []string, sm *openapi3.SerializationMethod, schemaRef *openapi3.SchemaRef) ([]any, error) { +func (d *urlValuesDecoder) parseArray(raw []string, schemaRef *openapi3.SchemaRef) ([]any, error) { var value []any for i, v := range raw { @@ -565,6 +567,12 @@ func (d *urlValuesDecoder) parseArray(raw []string, sm *openapi3.SerializationMe } value = append(value, item) } + // If the array has only one element and that element is an empty string, it means no value exists, so return nil. + if len(value) == 1 { + if str, ok := value[0].(string); ok && str == "" { + return nil, nil + } + } return value, nil } @@ -899,26 +907,6 @@ func deepSet(m map[string]any, keys []string, value any) { m[keys[len(keys)-1]] = value } -func findNestedSchema(parentSchema *openapi3.SchemaRef, keys []string) (*openapi3.SchemaRef, error) { - currentSchema := parentSchema - for _, key := range keys { - if currentSchema.Value.Type.Includes(openapi3.TypeArray) { - currentSchema = currentSchema.Value.Items - } else { - propertySchema, ok := currentSchema.Value.Properties[key] - if !ok { - if currentSchema.Value.AdditionalProperties.Schema == nil { - return nil, fmt.Errorf("nested schema for key %q not found", key) - } - currentSchema = currentSchema.Value.AdditionalProperties.Schema - continue - } - currentSchema = propertySchema - } - } - return currentSchema, nil -} - // makeObject returns an object that contains properties from props. func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string]any, error) { mobj := make(map[string]any) @@ -944,6 +932,13 @@ func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string return result, nil } +// maxSliceMapToSliceGap bounds how many synthesized nil holes sliceMapToSlice +// will fill in for a sparse array before rejecting the input. Without this, +// an attacker-supplied index (e.g. from a deepObject query parameter) drives +// an allocation proportional to the index itself, regardless of how many +// elements were actually provided. +const maxSliceMapToSliceGap = 10000 + // example: map[0:map[key:true] 1:map[key:false]] -> [map[key:true] map[key:false]] func sliceMapToSlice(m map[string]any) ([]any, error) { var result []any @@ -954,6 +949,9 @@ func sliceMapToSlice(m map[string]any) ([]any, error) { if err != nil { return nil, fmt.Errorf("array indexes must be integers: %w", err) } + if key < 0 { + return nil, fmt.Errorf("array indexes must not be negative: %d", key) + } keys = append(keys, key) } max := -1 @@ -962,6 +960,12 @@ func sliceMapToSlice(m map[string]any) ([]any, error) { max = k } } + // max+1 is the size of the slice this loop is about to build; bound the + // gap between what was actually supplied (len(m)) and that size so a + // single huge index can't force an outsized allocation. + if gap := max + 1 - len(m); gap > maxSliceMapToSliceGap { + return nil, fmt.Errorf("array index %d is too sparse relative to the %d supplied items", max, len(m)) + } for i := 0; i <= max; i++ { val, ok := m[strconv.Itoa(i)] if !ok { @@ -1071,9 +1075,7 @@ func buildFromSchemas(schemas openapi3.SchemaRefs, params map[string]any, mapKey if err == nil && val != nil { if m, ok := val.(map[string]any); ok { - for k, v := range m { - resultMap[k] = v - } + maps.Copy(resultMap, m) continue } @@ -1136,13 +1138,13 @@ func parseArray(raw []string, schemaRef *openapi3.SchemaRef) ([]any, error) { } // parsePrimitive returns a value that is created by parsing a source string to a primitive type -// that is specified by a schema. The function returns nil when the source string is empty. +// that is specified by a schema. The function returns nil when the source string is empty and the type is not "string". // The function panics when a schema has a non-primitive type. func parsePrimitive(raw string, schema *openapi3.SchemaRef) (v any, err error) { - if raw == "" { - return nil, nil - } for _, typ := range schema.Value.Type.Slice() { + if raw == "" && typ != "string" { + return nil, nil + } if v, err = parsePrimitiveCase(raw, schema, typ); err == nil { return } @@ -1231,7 +1233,64 @@ func UnregisterBodyDecoder(contentType string) { var headerCT = http.CanonicalHeaderKey("Content-Type") -const prefixUnsupportedCT = "unsupported content type" +const ( + prefixUnsupportedCT = "unsupported content type" + prefixNotMatchingCT = "not matching content types" +) + +func isBinary(schema *openapi3.SchemaRef) bool { + if schema == nil || schema.Value == nil { + return false + } + return schema.Value.Type.Is("string") && schema.Value.Format == "binary" +} + +func getEncodingContentType(encFn EncodingFn) string { + var enc *openapi3.Encoding + if encFn != nil { + // encFn is passed to decodeBody only in form body decoders as a subEncFn, so key can be "" + // func(string) *openapi3.Encoding { return enc } + enc = encFn("") + } + if enc == nil { + return "" + } + + return enc.ContentType +} + +// contentTypeAllowedByEncoding reports whether mediaType satisfies the +// encoding.contentType, which per OAS 3.0 may be a single media type, a wildcard +// (e.g. image/*), or a comma-separated list of those. mediaType must be a base +// type; parameters on encoding entries (e.g. "; charset=utf-8") are ignored. +func contentTypeAllowedByEncoding(mediaType, encodingContentType string) bool { + for raw := range strings.SplitSeq(encodingContentType, ",") { + want := strings.TrimSpace(raw) + if want == "" { + continue + } + if base, _, err := mime.ParseMediaType(want); err == nil { + want = base + } + if mediaTypeMatches(mediaType, want) { + return true + } + } + return false +} + +// mediaTypeMatches reports whether got matches want (exact, type/* or */* wildcard, +// case-insensitive). Both must be base media types without parameters. +func mediaTypeMatches(got, want string) bool { + if want == "*/*" || strings.EqualFold(want, got) { + return true + } + if prefix, ok := strings.CutSuffix(want, "/*"); ok { + gotType, _, found := strings.Cut(got, "/") + return found && strings.EqualFold(gotType, prefix) + } + return false +} // decodeBody returns a decoded body. // The function returns ParseError when a body is invalid. @@ -1246,9 +1305,36 @@ func decodeBody(body io.Reader, header http.Header, schema *openapi3.SchemaRef, contentType = "text/plain" } } + mediaType := parseMediaType(contentType) + encodingContentType := getEncodingContentType(encFn) + if isBinary(schema) && encodingContentType == "" { + value, err := FileBodyDecoder(body, header, schema, encFn) + return mediaType, value, err + } + + if encodingContentType != "" && + !contentTypeAllowedByEncoding(mediaType, encodingContentType) { + return "", nil, &ParseError{ + Kind: KindOther, + Reason: fmt.Sprintf( + "%s: header %q, encoding %q", + prefixNotMatchingCT, + mediaType, + encodingContentType, + ), + } + } + decoder, ok := bodyDecoders[mediaType] if !ok { + // A binary part with no registered decoder (e.g. image/png) is read as + // raw bytes: encoding.contentType restricts the accepted media types but + // does not require a registered decoder. + if isBinary(schema) { + value, err := FileBodyDecoder(body, header, schema, encFn) + return mediaType, value, err + } return "", nil, &ParseError{ Kind: KindUnsupportedFormat, Reason: fmt.Sprintf("%s %q", prefixUnsupportedCT, mediaType), @@ -1264,6 +1350,7 @@ func decodeBody(body io.Reader, header http.Header, schema *openapi3.SchemaRef, func init() { RegisterBodyDecoder("application/json", JSONBodyDecoder) RegisterBodyDecoder("application/json-patch+json", JSONBodyDecoder) + RegisterBodyDecoder("application/merge-patch+json", JSONBodyDecoder) RegisterBodyDecoder("application/ld+json", JSONBodyDecoder) RegisterBodyDecoder("application/hal+json", JSONBodyDecoder) RegisterBodyDecoder("application/vnd.api+json", JSONBodyDecoder) @@ -1312,7 +1399,13 @@ func UrlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3. if !schema.Value.Type.Is("object") { return nil, errors.New("unsupported schema of request body") } - for propName, propSchema := range schema.Value.Properties { + propNames := make([]string, 0, len(schema.Value.Properties)) + for name := range schema.Value.Properties { + propNames = append(propNames, name) + } + slices.Sort(propNames) + for _, propName := range propNames { + propSchema := schema.Value.Properties[propName] propType := propSchema.Value.Type switch { case propType.Is("object"): @@ -1362,9 +1455,15 @@ func decodeSchemaConstructs(dec *urlValuesDecoder, schemas []*openapi3.SchemaRef return err } - for name, prop := range schemaRef.Value.Properties { - value, _, err := decodeProperty(dec, name, prop, encFn) - if err != nil { + propNames := make([]string, 0, len(schemaRef.Value.Properties)) + for name := range schemaRef.Value.Properties { + propNames = append(propNames, name) + } + slices.Sort(propNames) + for _, name := range propNames { + prop := schemaRef.Value.Properties[name] + value, present, err := decodeProperty(dec, name, prop, encFn) + if err != nil || !present { continue } if existingValue, exists := obj[name]; exists && !isEqual(existingValue, value) { @@ -1459,36 +1558,40 @@ func MultipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.S } } + partHeader := http.Header(part.Header) var value any - if _, value, err = decodeBody(part, http.Header(part.Header), valueSchema, subEncFn); err != nil { + if _, value, err = decodeBody(part, partHeader, valueSchema, subEncFn); err != nil { if v, ok := err.(*ParseError); ok { return nil, &ParseError{path: []any{name}, Cause: v} } return nil, fmt.Errorf("part %s: %w", name, err) } + + // Parse primitive types when no content type is explicitly provided, or the content type is set to text/plain + if contentType := partHeader.Get(headerCT); contentType == "" || contentType == "text/plain" { + if value, err = parsePrimitive(value.(string), valueSchema); err != nil { + if v, ok := err.(*ParseError); ok { + return nil, &ParseError{path: []any{name}, Cause: v} + } + return nil, fmt.Errorf("part %s: %w", name, err) + } + } + values[name] = append(values[name], value) } allTheProperties := make(map[string]*openapi3.SchemaRef) if len(schema.Value.AllOf) > 0 { for _, sr := range schema.Value.AllOf { - for k, v := range sr.Value.Properties { - allTheProperties[k] = v - } + maps.Copy(allTheProperties, sr.Value.Properties) if addProps := sr.Value.AdditionalProperties.Schema; addProps != nil { - for k, v := range addProps.Value.Properties { - allTheProperties[k] = v - } + maps.Copy(allTheProperties, addProps.Value.Properties) } } } else { - for k, v := range schema.Value.Properties { - allTheProperties[k] = v - } + maps.Copy(allTheProperties, schema.Value.Properties) if addProps := schema.Value.AdditionalProperties.Schema; addProps != nil { - for k, v := range addProps.Value.Properties { - allTheProperties[k] = v - } + maps.Copy(allTheProperties, addProps.Value.Properties) } } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request.go index 13e2247a8..e4d944286 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request.go @@ -8,7 +8,7 @@ import ( "io" "net/http" "net/url" - "sort" + "slices" "strings" "github.com/getkin/kin-openapi/openapi3" @@ -90,8 +90,23 @@ func ValidateRequest(ctx context.Context, input *RequestValidationInput) error { // RequestBody requestBody := operation.RequestBody - if requestBody != nil && !options.ExcludeRequestBody { - if err := ValidateRequestBody(ctx, input, requestBody.Value); err != nil { + if !options.ExcludeRequestBody { + // Validate specification request body if present + if requestBody != nil { + if err := ValidateRequestBody(ctx, input, requestBody.Value); err != nil { + if !options.MultiError { + return err + } + me = append(me, err) + } + } + + // Reject if specification request body if not present (not wanted) but is present in the HTTP request + if options.RejectWhenRequestBodyNotSpecified && input.Request.ContentLength > 0 { + err := &RequestError{ + Input: input, + Err: errors.New("request body not allowed for this request"), + } if !options.MultiError { return err } @@ -219,12 +234,14 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param var opts []openapi3.SchemaValidationOption if options.MultiError { - opts = make([]openapi3.SchemaValidationOption, 0, 1) opts = append(opts, openapi3.MultiErrors()) } if options.customSchemaErrorFunc != nil { opts = append(opts, openapi3.SetSchemaErrorMessageCustomizer(options.customSchemaErrorFunc)) } + if input.Route != nil && input.Route.Spec.IsOpenAPI31OrLater() { + opts = append(opts, openapi3.EnableJSONSchema2020()) + } if err = schema.VisitJSON(value, opts...); err != nil { return &RequestError{Input: input, Parameter: parameter, Err: err} } @@ -315,7 +332,7 @@ func ValidateRequestBody(ctx context.Context, input *RequestValidationInput, req } defaultsSet := false - opts := make([]openapi3.SchemaValidationOption, 0, 4) // 4 potential opts here + var opts []openapi3.SchemaValidationOption opts = append(opts, openapi3.VisitAsRequest()) if !options.SkipSettingDefaults { opts = append(opts, openapi3.DefaultsSet(func() { defaultsSet = true })) @@ -332,6 +349,11 @@ func ValidateRequestBody(ctx context.Context, input *RequestValidationInput, req if options.RegexCompiler != nil { opts = append(opts, openapi3.SetSchemaRegexCompiler(options.RegexCompiler)) } + // Append additional schema validation options (e.g., document-scoped format validators) + opts = append(opts, options.SchemaValidationOptions...) + if input.Route != nil && input.Route.Spec.IsOpenAPI31OrLater() { + opts = append(opts, openapi3.EnableJSONSchema2020()) + } // Validate JSON with the schema if err := contentType.Schema.Value.VisitJSON(value, opts...); err != nil { @@ -399,7 +421,7 @@ func validateSecurityRequirement(ctx context.Context, input *RequestValidationIn for name := range securityRequirement { names = append(names, name) } - sort.Strings(names) + slices.Sort(names) // Get authentication function options := input.Options @@ -423,8 +445,7 @@ func validateSecurityRequirement(ctx context.Context, input *RequestValidationIn defer input.Request.Body.Close() var err error - data, err = io.ReadAll(input.Request.Body) - if err != nil { + if data, err = io.ReadAll(input.Request.Body); err != nil { return &RequestError{ Input: input, Reason: "reading failed", diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_response.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_response.go index a2dcc03a3..d055c7173 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_response.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_response.go @@ -7,7 +7,7 @@ import ( "fmt" "io" "net/http" - "sort" + "slices" "strings" "github.com/getkin/kin-openapi/openapi3" @@ -20,9 +20,7 @@ import ( // Note: One can tune the behavior of uniqueItems: true verification // by registering a custom function with openapi3.RegisterArrayUniqueItemsChecker func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error { - req := input.RequestValidationInput.Request - switch req.Method { - case "HEAD": + if req := input.RequestValidationInput.Request; req.Method == http.MethodHead { return nil } status := input.Status @@ -63,7 +61,7 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error return &ResponseError{Input: input, Reason: "response has not been resolved"} } - opts := make([]openapi3.SchemaValidationOption, 0, 3) // 3 potential options here + var opts []openapi3.SchemaValidationOption if options.MultiError { opts = append(opts, openapi3.MultiErrors()) } @@ -73,6 +71,11 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error if options.ExcludeWriteOnlyValidations { opts = append(opts, openapi3.DisableWriteOnlyValidation()) } + // Append additional schema validation options (e.g., document-scoped format validators) + opts = append(opts, options.SchemaValidationOptions...) + if route.Spec.IsOpenAPI31OrLater() { + opts = append(opts, openapi3.EnableJSONSchema2020()) + } headers := make([]string, 0, len(response.Headers)) for k := range response.Headers { @@ -80,7 +83,7 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error headers = append(headers, k) } } - sort.Strings(headers) + slices.Sort(headers) for _, headerName := range headers { headerRef := response.Headers[headerName] if err := validateResponseHeader(headerName, headerRef, input, opts); err != nil { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/validation_error_encoder.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/validation_error_encoder.go index 4369ca6b3..cef4b48d9 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/validation_error_encoder.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/validation_error_encoder.go @@ -117,16 +117,24 @@ func convertParseError(e *RequestError, innerErr *ParseError) *ValidationError { } } else if innerErr.RootCause() != nil { if rootErr, ok := innerErr.Cause.(*ParseError); ok && - rootErr.Kind == KindInvalidFormat && e.Parameter.In == "query" { + rootErr.Kind == KindInvalidFormat && e.Parameter != nil && e.Parameter.In == "query" { return &ValidationError{ Status: http.StatusBadRequest, Title: fmt.Sprintf("parameter %q in %s is invalid: %v is %s", e.Parameter.Name, e.Parameter.In, rootErr.Value, rootErr.Reason), } } + // For body parse errors (e.Parameter == nil) the outer ParseError's + // Reason is often empty, e.g. the multipart decoder wraps a part's + // *ParseError without setting one. Fall back to the full error text so + // the response still carries a meaningful message. + title := innerErr.Reason + if title == "" { + title = innerErr.Error() + } return &ValidationError{ Status: http.StatusBadRequest, - Title: innerErr.Reason, + Title: title, } } return nil diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/validation_kit.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/validation_kit.go index 9e11e4fc8..16951e476 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/validation_kit.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/validation_kit.go @@ -81,5 +81,5 @@ func DefaultErrorEncoder(_ context.Context, err error, w http.ResponseWriter) { code = sc.StatusCode() } w.WriteHeader(code) - w.Write(body) + _, _ = w.Write(body) } diff --git a/vendor/github.com/getkin/kin-openapi/routers/gorillamux/router.go b/vendor/github.com/getkin/kin-openapi/routers/gorillamux/router.go index 2d092426a..0ccfedaec 100644 --- a/vendor/github.com/getkin/kin-openapi/routers/gorillamux/router.go +++ b/vendor/github.com/getkin/kin-openapi/routers/gorillamux/router.go @@ -10,7 +10,7 @@ import ( "net/http" "net/url" "regexp" - "sort" + "slices" "strings" "github.com/gorilla/mux" @@ -69,7 +69,7 @@ func NewRouter(doc *openapi3.T) (routers.Router, error) { for method := range operations { methods = append(methods, method) } - sort.Strings(methods) + slices.Sort(methods) for _, s := range servers { muxRoute := muxRouter.Path(s.base + path).Methods(methods...) @@ -104,7 +104,7 @@ func (r *Router) FindRoute(req *http.Request) (*routers.Route, map[string]string for i, m := range r.muxes { var match mux.RouteMatch if m.muxRoute.Match(req, &match) { - if err := match.MatchErr; err != nil { + if err := match.MatchErr; err != nil { //nolint:staticcheck // What then? } vars := match.Vars @@ -180,7 +180,7 @@ func makeServers(in openapi3.Servers) ([]srv, error) { func newSrv(serverURL string, server *openapi3.Server, varsUpdater varsf) (srv, error) { var schemes []string if strings.Contains(serverURL, "://") { - scheme0 := strings.Split(serverURL, "://")[0] + scheme0, _, _ := strings.Cut(serverURL, "://") schemes = permutePart(scheme0, server) serverURL = strings.Replace(serverURL, scheme0+"://", schemes[0]+"://", 1) } @@ -207,13 +207,13 @@ func newSrv(serverURL string, server *openapi3.Server, varsUpdater varsf) (srv, var blURL, brURL = strings.Repeat("-", 50), strings.Repeat("_", 50) func bEncode(s string) string { - s = strings.Replace(s, "{", blURL, -1) - s = strings.Replace(s, "}", brURL, -1) + s = strings.ReplaceAll(s, "{", blURL) + s = strings.ReplaceAll(s, "}", brURL) return s } func bDecode(s string) string { - s = strings.Replace(s, blURL, "{", -1) - s = strings.Replace(s, brURL, "}", -1) + s = strings.ReplaceAll(s, blURL, "{") + s = strings.ReplaceAll(s, brURL, "}") return s } @@ -240,6 +240,7 @@ func permutePart(part0 string, srv *openapi3.Server) []string { for value := range m { s = append(s, value) } + slices.Sort(s) var2val[name] = mapAndSlice{m: m, s: s} } if len(var2val) == 0 { @@ -250,7 +251,7 @@ func permutePart(part0 string, srv *openapi3.Server) []string { for i := 0; i < max; i++ { part := part0 for name, mas := range var2val { - part = strings.Replace(part, name, mas.s[i%len(mas.s)], -1) + part = strings.ReplaceAll(part, name, mas.s[i%len(mas.s)]) } partsMap[part] = struct{}{} } @@ -258,6 +259,6 @@ func permutePart(part0 string, srv *openapi3.Server) []string { for part := range partsMap { parts = append(parts, part) } - sort.Strings(parts) + slices.Sort(parts) return parts } diff --git a/vendor/github.com/getkin/kin-openapi/routers/legacy/pathpattern/node.go b/vendor/github.com/getkin/kin-openapi/routers/legacy/pathpattern/node.go index 75932a26d..401e04c33 100644 --- a/vendor/github.com/getkin/kin-openapi/routers/legacy/pathpattern/node.go +++ b/vendor/github.com/getkin/kin-openapi/routers/legacy/pathpattern/node.go @@ -10,9 +10,10 @@ package pathpattern import ( "bytes" + "cmp" "fmt" "regexp" - "sort" + "slices" "strings" ) @@ -133,24 +134,14 @@ func (suffix Suffix) String() string { type SuffixList []Suffix -func (list SuffixList) Less(i, j int) bool { - a, b := list[i], list[j] - ak, bk := a.Kind, b.Kind - if ak < bk { - return true - } else if bk < ak { - return false +// compareSuffix compares two Suffix values for sorting purposes. +// It compares by Kind first, then by Pattern in reverse order. +func compareSuffix(a, b Suffix) int { + if c := cmp.Compare(a.Kind, b.Kind); c != 0 { + return c } - return a.Pattern > b.Pattern -} - -func (list SuffixList) Len() int { - return len(list) -} - -func (list SuffixList) Swap(i, j int) { - a, b := list[i], list[j] - list[i], list[j] = b, a + // Reverse order for Pattern + return cmp.Compare(b.Pattern, a.Pattern) } func (currentNode *Node) MustAdd(path string, value any, options *Options) { @@ -256,7 +247,7 @@ loop: newNode := &Node{} suffix.Node = newNode currentNode.Suffixes = append(currentNode.Suffixes, suffix) - sort.Sort(currentNode.Suffixes) + slices.SortFunc(currentNode.Suffixes, compareSuffix) currentNode = newNode continue loop } diff --git a/vendor/github.com/go-openapi/jsonpointer/.gitignore b/vendor/github.com/go-openapi/jsonpointer/.gitignore index 769c24400..d8f4186fe 100644 --- a/vendor/github.com/go-openapi/jsonpointer/.gitignore +++ b/vendor/github.com/go-openapi/jsonpointer/.gitignore @@ -1 +1,5 @@ -secrets.yml +*.out +*.cov +.idea +.env +.mcp.json diff --git a/vendor/github.com/go-openapi/jsonpointer/.golangci.yml b/vendor/github.com/go-openapi/jsonpointer/.golangci.yml index 22f8d21cc..9d2733176 100644 --- a/vendor/github.com/go-openapi/jsonpointer/.golangci.yml +++ b/vendor/github.com/go-openapi/jsonpointer/.golangci.yml @@ -1,61 +1,70 @@ -linters-settings: - govet: - check-shadowing: true - golint: - min-confidence: 0 - gocyclo: - min-complexity: 45 - maligned: - suggest-new: true - dupl: - threshold: 200 - goconst: - min-len: 2 - min-occurrences: 3 - +version: "2" linters: - enable-all: true + default: all disable: - - maligned - - unparam - - lll - - gochecknoinits - - gochecknoglobals + - depguard - funlen + - goconst - godox - - gocognit - - whitespace - - wsl - - wrapcheck - - testpackage + - gomodguard + - gomodguard_v2 + - exhaustruct - nlreturn - - gomnd - - exhaustivestruct - - goerr113 - - errorlint - - nestif - - godot - - gofumpt + - nonamedreturns + - noinlineerr - paralleltest - - tparallel + - recvcheck + - testpackage - thelper - - ifshort - - exhaustruct + - tparallel - varnamelen - - gci - - depguard - - errchkjson - - inamedparam - - nonamedreturns - - musttag - - ireturn - - forcetypeassert - - cyclop - # deprecated linters - - deadcode - - interfacer - - scopelint - - varcheck - - structcheck - - golint - - nosnakecase + - whitespace + - wrapcheck + - wsl + - wsl_v5 + settings: + dupl: + threshold: 200 + goconst: + min-len: 2 + min-occurrences: 3 + cyclop: + max-complexity: 20 + gocyclo: + min-complexity: 20 + exhaustive: + default-signifies-exhaustive: true + default-case-required: true + lll: + line-length: 180 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + - gofumpt + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ +issues: + # Maximum issues count per one linter. + # Set to 0 to disable. + # Default: 50 + max-issues-per-linter: 0 + # Maximum count of issues with the same text. + # Set to 0 to disable. + # Default: 3 + max-same-issues: 0 diff --git a/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md index 9322b065e..bac878f21 100644 --- a/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md +++ b/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md @@ -23,7 +23,9 @@ include: Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or + advances + * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic @@ -55,7 +57,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at ivan+abuse@flanders.co.nz. All +reported by contacting the project team at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. @@ -68,7 +70,7 @@ members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +available at [][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md b/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md new file mode 100644 index 000000000..de0bf5c04 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md @@ -0,0 +1,25 @@ +# Contributors + +- Repository: ['go-openapi/jsonpointer'] + +| Total Contributors | Total Contributions | +| --- | --- | +| 13 | 132 | + +| Username | All Time Contribution Count | All Commits | +| --- | --- | --- | +| @fredbi | 83 | | +| @casualjim | 33 | | +| @magodo | 3 | | +| @youyuanwu | 3 | | +| @alexandear | 2 | | +| @gaiaz-iusipov | 1 | | +| @gbjk | 1 | | +| @gordallott | 1 | | +| @ianlancetaylor | 1 | | +| @mfleader | 1 | | +| @Neo2308 | 1 | | +| @olivierlemasle | 1 | | +| @testwill | 1 | | + + _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ diff --git a/vendor/github.com/go-openapi/jsonpointer/LICENSE b/vendor/github.com/go-openapi/jsonpointer/LICENSE index d64569567..261eeb9e9 100644 --- a/vendor/github.com/go-openapi/jsonpointer/LICENSE +++ b/vendor/github.com/go-openapi/jsonpointer/LICENSE @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/vendor/github.com/go-openapi/jsonpointer/NOTICE b/vendor/github.com/go-openapi/jsonpointer/NOTICE new file mode 100644 index 000000000..201908d2f --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/NOTICE @@ -0,0 +1,39 @@ +Copyright 2015-2025 go-swagger maintainers + +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +This software library, github.com/go-openapi/jsonpointer, includes software developed +by the go-swagger and go-openapi maintainers ("go-swagger maintainers"). + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this software except in compliance with the License. + +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0. + +This software is copied from, derived from, and inspired by other original software products. +It ships with copies of other software which license terms are recalled below. + +The original software was authored on 25-02-2013 by sigu-399 (https://github.com/sigu-399, sigu.399@gmail.com). + +github.com/sigu-399/jsonpointer +=========================== + +// SPDX-FileCopyrightText: Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) +// SPDX-License-Identifier: Apache-2.0 + +Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/go-openapi/jsonpointer/README.md b/vendor/github.com/go-openapi/jsonpointer/README.md index 0108f1d57..6e7929c64 100644 --- a/vendor/github.com/go-openapi/jsonpointer/README.md +++ b/vendor/github.com/go-openapi/jsonpointer/README.md @@ -1,19 +1,177 @@ -# gojsonpointer [![Build Status](https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonpointer/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) +# jsonpointer -[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) -[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) -[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer.svg)](https://pkg.go.dev/github.com/go-openapi/jsonpointer) -[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonpointer)](https://goreportcard.com/report/github.com/go-openapi/jsonpointer) + +[![Tests][test-badge]][test-url] [![Coverage][cov-badge]][cov-url] [![CI vuln scan][vuln-scan-badge]][vuln-scan-url] [![CodeQL][codeql-badge]][codeql-url] + + + +[![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url] + + +[![GoDoc][godoc-badge]][godoc-url] [![Discord Channel][discord-badge]][discord-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge] -An implementation of JSON Pointer - Go language +--- + +An implementation of JSON Pointer for golang, which supports go `struct`. + +## Announcements + +* **2026-07-07** : landing v1.0.0 + * stable API pledge + +* **2026-06-29** : reinsourced external dependency to swag (v0.24.0) + * module `github.com/go-openapi/swag/jsonname` is source directly here, so we no longer have any external dependency + * `jsonname` was never really used by any other package, so it makes sense to deprecate it away from the `swag` family + and retrofit its functionality here. `jsonpointer` no longer get external dependencies, besides test dependencies. + +* **2026-04-15** : added support for trailing "-" for arrays (v0.23.0) + * this brings full support of [RFC6901][RFC6901] + * this is supported for types relying on the reflection-based implemented + * API semantics remain essentially unaltered. Exception: `Pointer.Set(document any,value any) (document any, err error)` + can only perform a best-effort to mutate the input document in place. In the case of adding elements to an array with a + trailing "-", either pass a mutable array (`*[]T`) as the input document, or use the returned updated document instead. + * types that implement the `JSONSetable` interface may not implement the mutation implied by the trailing "-" + +* **2026-04-15** : added support for optional alternate JSON name providers + * for struct support the defaults might not suit all situations: there are known limitations + when it comes to handle untagged fields or embedded types. + * the default name provider in use is not fully aligned with go JSON stdlib + * exposed an option (or global setting) to change the provider that resolves a struct into json keys + * the default behavior is not altered ## Status -Completed YES -Tested YES +API is stable and feature-complete. + +The project continues to receive regular updates, bug fixes and hygiene maintenance (CI, linting, etc). + +## Import this library in your project + +```cmd +go get github.com/go-openapi/jsonpointer +``` + +## Basic usage + +See also some [examples](./examples_test.go) + +### Retrieving a value + +```go + import ( + "github.com/go-openapi/jsonpointer" + ) + + + var doc any + + ... + + pointer, err := jsonpointer.New("/foo/1") + if err != nil { + ... // error: e.g. invalid JSON pointer specification + } + + value, kind, err := pointer.Get(doc) + if err != nil { + ... // error: e.g. key not found, index out of bounds, etc. + } + + ... +``` + +### Setting a value + +```go + ... + var doc any + ... + pointer, err := jsonpointer.New("/foo/1") + if err != nil { + ... // error: e.g. invalid JSON pointer specification + } + + doc, err = p.Set(doc, "value") + if err != nil { + ... // error: e.g. key not found, index out of bounds, etc. + } +``` + +## Change log + +See ## References -http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07 -### Note -The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, the reference token MUST contain either...' is not implemented. + + +also known as [RFC6901][RFC6901]. + +## Licensing + +This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). + +See the license [NOTICE](./NOTICE), which recalls the licensing terms of all the pieces of software +on top of which it has been built. + +## Limitations + +* [RFC6901][RFC6901] is now fully supported, including trailing "-" semantics for arrays (for `Set` operations). +* Default behavior: JSON name detection in go `struct`s + - Unlike go standard marshaling, untagged fields do not default to the go field name and are ignored. + - anonymous fields are not traversed if untagged + - the above limitations may be overcome by calling `UseGoNameProvider()` at initialization time. + - alternatively, users may inject the desired custom behavior for naming fields as an option. + +## Other documentation + +* [All-time contributors](./CONTRIBUTORS.md) +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] + +## Cutting a new release + +Maintainers can cut a new release by either: + +* running [this workflow](https://github.com/go-openapi/jsonpointer/actions/workflows/bump-release.yml) +* or pushing a semver tag + * signed tags are preferred + * The tag message is prepended to release notes + + +[test-badge]: https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg +[test-url]: https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml +[cov-badge]: https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg +[cov-url]: https://codecov.io/gh/go-openapi/jsonpointer +[vuln-scan-badge]: https://github.com/go-openapi/jsonpointer/actions/workflows/scanner.yml/badge.svg +[vuln-scan-url]: https://github.com/go-openapi/jsonpointer/actions/workflows/scanner.yml +[codeql-badge]: https://github.com/go-openapi/jsonpointer/actions/workflows/codeql.yml/badge.svg +[codeql-url]: https://github.com/go-openapi/jsonpointer/actions/workflows/codeql.yml + +[release-badge]: https://badge.fury.io/gh/go-openapi%2Fjsonpointer.svg +[release-url]: https://badge.fury.io/gh/go-openapi%2Fjsonpointer + +[gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/jsonpointer +[gocard-url]: https://goreportcard.com/report/github.com/go-openapi/jsonpointer +[codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/jsonpointer +[codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/jsonpointer + +[godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer +[godoc-url]: http://pkg.go.dev/github.com/go-openapi/jsonpointer +[discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue +[discord-url]: https://discord.gg/FfnFYaC3k5 + + +[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg +[license-url]: https://github.com/go-openapi/jsonpointer/?tab=Apache-2.0-1-ov-file#readme + +[goversion-badge]: https://img.shields.io/github/go-mod/go-version/go-openapi/jsonpointer +[goversion-url]: https://github.com/go-openapi/jsonpointer/blob/master/go.mod +[top-badge]: https://img.shields.io/github/languages/top/go-openapi/jsonpointer +[commits-badge]: https://img.shields.io/github/commits-since/go-openapi/jsonpointer/latest +[RFC6901]: https://www.rfc-editor.org/rfc/rfc6901 + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/vendor/github.com/go-openapi/jsonpointer/SECURITY.md b/vendor/github.com/go-openapi/jsonpointer/SECURITY.md new file mode 100644 index 000000000..1fea2c573 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/SECURITY.md @@ -0,0 +1,37 @@ +# Security Policy + +This policy outlines the commitment and practices of the go-openapi maintainers regarding security. + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| O.x | :white_check_mark: | + +## Vulnerability checks in place + +This repository uses automated vulnerability scans, at every merged commit and at least once a week. + +We use: + +* [`GitHub CodeQL`][codeql-url] +* [`trivy`][trivy-url] +* [`govulncheck`][govulncheck-url] + +Reports are centralized in github security reports and visible only to the maintainers. + +## Reporting a vulnerability + +If you become aware of a security vulnerability that affects the current repository, +**please report it privately to the maintainers** +rather than opening a publicly visible GitHub issue. + +Please follow the instructions provided by github to [Privately report a security vulnerability][github-guidance-url]. + +> [!NOTE] +> On Github, navigate to the project's "Security" tab then click on "Report a vulnerability". + +[codeql-url]: https://github.com/github/codeql +[trivy-url]: https://trivy.dev/docs/latest/getting-started +[govulncheck-url]: https://go.dev/blog/govulncheck +[github-guidance-url]: https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability diff --git a/vendor/github.com/go-openapi/jsonpointer/errors.go b/vendor/github.com/go-openapi/jsonpointer/errors.go new file mode 100644 index 000000000..2ae6e3cfb --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/errors.go @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonpointer + +import "fmt" + +type pointerError string + +func (e pointerError) Error() string { + return string(e) +} + +const ( + // ErrPointer is a sentinel error raised by all errors from this package. + ErrPointer pointerError = "JSON pointer error" + + // ErrInvalidStart states that a JSON pointer must start with a separator ("/"). + ErrInvalidStart pointerError = `JSON pointer must be empty or start with a "` + pointerSeparator + `"` + + // ErrUnsupportedValueType indicates that a value of the wrong type is being set. + ErrUnsupportedValueType pointerError = "only structs, pointers, maps and slices are supported for setting values" + + // ErrDashToken indicates use of the RFC 6901 "-" reference token in a context where it cannot be + // resolved. + // + // Per RFC 6901 §4 the "-" token refers to the (nonexistent) element after the last array element. + // It may only be used as the terminal token of a [Pointer.Set] against a slice, where it means + // "append". + // + // Any other use (get, offset, intermediate traversal, non-slice target) is an error condition that + // wraps this sentinel. + ErrDashToken pointerError = `the "-" array token cannot be resolved here` //nolint:gosec // G101 false positive: this is a JSON Pointer reference token, not a credential. +) + +const dashToken = "-" + +func errNoKey(key string) error { + return fmt.Errorf("object has no key %q: %w", key, ErrPointer) +} + +func errOutOfBounds(length, idx int) error { + return fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", length-1, idx, ErrPointer) +} + +func errInvalidReference(token string) error { + return fmt.Errorf("invalid token reference %q: %w", token, ErrPointer) +} + +func errDashOnGet() error { + return fmt.Errorf("cannot resolve %q token on get: %w: %w", dashToken, ErrDashToken, ErrPointer) +} + +func errDashIntermediate() error { + return fmt.Errorf("the %q token may only appear as the terminal token of a pointer: %w: %w", dashToken, ErrDashToken, ErrPointer) +} + +func errDashOnOffset() error { + return fmt.Errorf("cannot compute offset for %q token (nonexistent element): %w: %w", dashToken, ErrDashToken, ErrPointer) +} diff --git a/vendor/github.com/go-openapi/jsonpointer/ifaces.go b/vendor/github.com/go-openapi/jsonpointer/ifaces.go new file mode 100644 index 000000000..31359c48f --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/ifaces.go @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonpointer + +import "reflect" + +// JSONPointable is an interface for structs to implement, when they need to customize the json +// pointer process or want to avoid the use of reflection. +type JSONPointable interface { + // JSONLookup returns a value pointed at this (unescaped) key. + JSONLookup(key string) (any, error) +} + +// JSONSetable is an interface for structs to implement, when they need to customize the json +// pointer process or want to avoid the use of reflection. +// +// # Handling of the RFC 6901 "-" token +// +// When a type implementing JSONSetable is the terminal parent of a [Pointer.Set] call, the library +// passes the raw reference token to JSONSet without interpretation. +// +// In particular, the RFC 6901 "-" token (which conventionally means "append" for arrays, per RFC +// 6902) is forwarded verbatim as the key argument. +// +// Implementations that model an array-like container are expected to give "-" the append semantics; +// implementations that do not should return an error wrapping [ErrDashToken] (or [ErrPointer]) for +// clarity. +// +// Implementations are responsible for any in-place mutation: the library does not attempt to rebind +// the result of JSONSet into a parent container. +type JSONSetable interface { + // JSONSet sets the value pointed at the (unescaped) key. + // + // The key may be the RFC 6901 "-" token when the pointer targets a slice-like member; see the + // interface documentation for details. + JSONSet(key string, value any) error +} + +// NameProvider knows how to resolve go struct fields into json names. +// +// The default provider is brought by +// [github.com/go-openapi/jsonpointer/jsonname.DefaultJSONNameProvider]. +type NameProvider interface { + // GetGoName gets the go name for a json property name + GetGoName(subject any, name string) (string, bool) + + // GetGoNameForType gets the go name for a given type for a json property name + GetGoNameForType(tpe reflect.Type, name string) (string, bool) +} diff --git a/vendor/github.com/go-openapi/jsonpointer/jsonname/doc.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/doc.go new file mode 100644 index 000000000..79232eaca --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/doc.go @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package jsonname is a provider of json property names from go properties. +package jsonname diff --git a/vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go new file mode 100644 index 000000000..5eec18fbf --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go @@ -0,0 +1,288 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonname + +import ( + "reflect" + "strings" + "sync" +) + +var _ providerIface = (*GoNameProvider)(nil) + +// GoNameProvider resolves json property names to go struct field names following the same rules as +// the standard library's [encoding/json] package. +// +// Contrary to [NameProvider], it considers exported fields without a json tag, and promotes fields +// from anonymous embedded struct types. +// +// Rules (aligned with encoding/json): +// +// - unexported fields are ignored; +// - a field tagged `json:"-"` is ignored; +// - a field tagged `json:"-,"` is kept under the json name "-" (stdlib quirk); +// - a field tagged `json:""` or with no json tag at all keeps its Go name as json name; +// - anonymous struct fields without an explicit json tag have their fields +// promoted into the parent, following breadth-first depth rules: +// a shallower field wins over a deeper one; at equal depth, a conflict +// discards all conflicting fields unless exactly one has an explicit json tag. +// +// This type is safe for concurrent use. +type GoNameProvider struct { + lock sync.Mutex + index map[reflect.Type]nameIndex +} + +// NewGoNameProvider creates a new [GoNameProvider]. +func NewGoNameProvider() *GoNameProvider { + return &GoNameProvider{ + index: make(map[reflect.Type]nameIndex), + } +} + +// GetJSONNames gets all the json property names for a type. +func (n *GoNameProvider) GetJSONNames(subject any) []string { + n.lock.Lock() + defer n.lock.Unlock() + + tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() + names := n.nameIndexFor(tpe) + + res := make([]string, 0, len(names.jsonNames)) + for k := range names.jsonNames { + res = append(res, k) + } + + return res +} + +// GetJSONName gets the json name for a go property name. +func (n *GoNameProvider) GetJSONName(subject any, name string) (string, bool) { + tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() + + return n.GetJSONNameForType(tpe, name) +} + +// GetJSONNameForType gets the json name for a go property name on a given type. +func (n *GoNameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + + names := n.nameIndexFor(tpe) + nme, ok := names.goNames[name] + + return nme, ok +} + +// GetGoName gets the go name for a json property name. +func (n *GoNameProvider) GetGoName(subject any, name string) (string, bool) { + tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() + + return n.GetGoNameForType(tpe, name) +} + +// GetGoNameForType gets the go name for a given type for a json property name. +func (n *GoNameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + + names := n.nameIndexFor(tpe) + nme, ok := names.jsonNames[name] + + return nme, ok +} + +func (n *GoNameProvider) nameIndexFor(tpe reflect.Type) nameIndex { + if names, ok := n.index[tpe]; ok { + return names + } + + names := buildGoNameIndex(tpe) + n.index[tpe] = names + + return names +} + +// fieldEntry captures a candidate field discovered while walking a struct along with the +// indirection path from the root type (used to resolve conflicts by depth in the same way +// encoding/json does). +type fieldEntry struct { + goName string + jsonName string + index []int + tagged bool +} + +func buildGoNameIndex(tpe reflect.Type) nameIndex { + fields := collectGoFields(tpe) + + idx := make(map[string]string, len(fields)) + reverseIdx := make(map[string]string, len(fields)) + for _, f := range fields { + idx[f.jsonName] = f.goName + reverseIdx[f.goName] = f.jsonName + } + + return nameIndex{jsonNames: idx, goNames: reverseIdx} +} + +// collectGoFields walks tpe breadth-first along anonymous struct fields, +// reproducing the field selection performed by encoding/json.typeFields. +// +//nolint:gocognit // everything is inlined to help the compiler determine what escapes and what doesn't +func collectGoFields(tpe reflect.Type) []fieldEntry { + if tpe.Kind() != reflect.Struct { + return nil + } + + type queued struct { + typ reflect.Type + index []int + } + + current := []queued{} + next := []queued{{typ: tpe}} + visited := map[reflect.Type]bool{tpe: true} + + var ( + candidates []fieldEntry + count = map[string]int{} + nextCount = map[string]int{} + ) + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, count + for k := range nextCount { + delete(nextCount, k) + } + + for _, q := range current { + for i := range q.typ.NumField() { + sf := q.typ.Field(i) + + if sf.Anonymous { + ft := sf.Type + if ft.Kind() == reflect.Pointer { + ft = ft.Elem() + } + if !sf.IsExported() && ft.Kind() != reflect.Struct { + continue + } + } else if !sf.IsExported() { + continue + } + + tag := sf.Tag.Get("json") + if tag == "-" { + continue + } + jsonName, _ := parseJSONTag(tag) + tagged := jsonName != "" + + ft := sf.Type + if ft.Kind() == reflect.Pointer { + ft = ft.Elem() + } + + if sf.Anonymous && ft.Kind() == reflect.Struct && !tagged { + if visited[ft] { + continue + } + visited[ft] = true + + index := make([]int, len(q.index)+1) + copy(index, q.index) + index[len(q.index)] = i + next = append(next, queued{typ: ft, index: index}) + + continue + } + + name := jsonName + if name == "" { + name = sf.Name + } + + index := make([]int, len(q.index)+1) + copy(index, q.index) + index[len(q.index)] = i + + candidates = append(candidates, fieldEntry{ + goName: sf.Name, + jsonName: name, + index: index, + tagged: tagged, + }) + nextCount[name]++ + } + } + } + + return dominantFields(candidates) +} + +// dominantFields applies the Go encoding/json conflict resolution rules: at each JSON name, the +// shallowest field wins; at equal depth, a uniquely tagged candidate wins; otherwise all candidates +// for that name are dropped. +func dominantFields(candidates []fieldEntry) []fieldEntry { + byName := make(map[string][]fieldEntry, len(candidates)) + for _, c := range candidates { + byName[c.jsonName] = append(byName[c.jsonName], c) + } + + out := make([]fieldEntry, 0, len(byName)) + for _, group := range byName { + if len(group) == 1 { + out = append(out, group[0]) + + continue + } + + minDepth := len(group[0].index) + for _, c := range group[1:] { + if len(c.index) < minDepth { + minDepth = len(c.index) + } + } + + var shallow []fieldEntry + for _, c := range group { + if len(c.index) == minDepth { + shallow = append(shallow, c) + } + } + + if len(shallow) == 1 { + out = append(out, shallow[0]) + + continue + } + + var tagged []fieldEntry + for _, c := range shallow { + if c.tagged { + tagged = append(tagged, c) + } + } + if len(tagged) == 1 { + out = append(out, tagged[0]) + } + } + + return out +} + +// parseJSONTag returns the name component of a json struct tag and whether it carried any non-name +// option (kept for future-proofing, e.g. "omitempty"). +func parseJSONTag(tag string) (string, string) { + if tag == "" { + return "", "" + } + if before, after, ok := strings.Cut(tag, ","); ok { + return before, after + } + + return tag, "" +} diff --git a/vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go new file mode 100644 index 000000000..64871f0d2 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonname + +import "reflect" + +// providerIface is an unexported compile-time contract that every name provider in this package is +// expected to satisfy. +// +// It mirrors the interface declared by the main consumer of this module: +// [github.com/go-openapi/jsonpointer.NameProvider]. +type providerIface interface { + GetGoName(subject any, name string) (string, bool) + GetGoNameForType(tpe reflect.Type, name string) (string, bool) +} diff --git a/vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go new file mode 100644 index 000000000..1bec2406b --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonname + +import ( + "reflect" + "strings" + "sync" +) + +// DefaultJSONNameProvider is the default cache for types. +var DefaultJSONNameProvider = NewNameProvider() //nolint:gochecknoglobals // default settings, for backward compatible package-level settings + +var _ providerIface = (*NameProvider)(nil) + +// NameProvider represents an object capable of translating from go property names to json property +// names. +// +// This type is thread-safe. +// +// See [github.com/go-openapi/jsonpointer.Pointer] for an example. +type NameProvider struct { + lock *sync.Mutex + index map[reflect.Type]nameIndex +} + +type nameIndex struct { + jsonNames map[string]string + goNames map[string]string +} + +// NewNameProvider creates a new name provider. +func NewNameProvider() *NameProvider { + return &NameProvider{ + lock: &sync.Mutex{}, + index: make(map[reflect.Type]nameIndex), + } +} + +func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) { + for i := range tpe.NumField() { + targetDes := tpe.Field(i) + + if targetDes.PkgPath != "" { // unexported + continue + } + + if targetDes.Anonymous { // walk embedded structures tree down first + buildnameIndex(targetDes.Type, idx, reverseIdx) + continue + } + + if tag := targetDes.Tag.Get("json"); tag != "" { + + parts := strings.Split(tag, ",") + if len(parts) == 0 { + continue + } + + nm := parts[0] + if nm == "-" { + continue + } + if nm == "" { // empty string means we want to use the Go name + nm = targetDes.Name + } + + idx[nm] = targetDes.Name + reverseIdx[targetDes.Name] = nm + } + } +} + +func newNameIndex(tpe reflect.Type) nameIndex { + idx := make(map[string]string, tpe.NumField()) + reverseIdx := make(map[string]string, tpe.NumField()) + + buildnameIndex(tpe, idx, reverseIdx) + return nameIndex{jsonNames: idx, goNames: reverseIdx} +} + +// GetJSONNames gets all the json property names for a type. +func (n *NameProvider) GetJSONNames(subject any) []string { + n.lock.Lock() + defer n.lock.Unlock() + tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() + names, ok := n.index[tpe] + if !ok { + names = n.makeNameIndex(tpe) + } + + res := make([]string, 0, len(names.jsonNames)) + for k := range names.jsonNames { + res = append(res, k) + } + return res +} + +// GetJSONName gets the json name for a go property name. +func (n *NameProvider) GetJSONName(subject any, name string) (string, bool) { + tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() + return n.GetJSONNameForType(tpe, name) +} + +// GetJSONNameForType gets the json name for a go property name on a given type. +func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + names, ok := n.index[tpe] + if !ok { + names = n.makeNameIndex(tpe) + } + nme, ok := names.goNames[name] + return nme, ok +} + +// GetGoName gets the go name for a json property name. +func (n *NameProvider) GetGoName(subject any, name string) (string, bool) { + tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() + return n.GetGoNameForType(tpe, name) +} + +// GetGoNameForType gets the go name for a given type for a json property name. +func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + names, ok := n.index[tpe] + if !ok { + names = n.makeNameIndex(tpe) + } + nme, ok := names.jsonNames[name] + return nme, ok +} + +func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex { + names := newNameIndex(tpe) + n.index[tpe] = names + return names +} diff --git a/vendor/github.com/go-openapi/jsonpointer/options.go b/vendor/github.com/go-openapi/jsonpointer/options.go new file mode 100644 index 000000000..223c1e5ff --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/options.go @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonpointer + +import ( + "sync" + + "github.com/go-openapi/jsonpointer/jsonname" +) + +// Option to tune the behavior of a JSON [Pointer]. +type Option func(*options) + +var ( + //nolint:gochecknoglobals // package level defaults are provided as a convenient, backward-compatible way to adopt options. + defaultOptions = options{ + provider: jsonname.DefaultJSONNameProvider, + } + //nolint:gochecknoglobals // guards defaultOptions against concurrent SetDefaultNameProvider / read races (testing) + defaultOptionsMu sync.RWMutex +) + +// SetDefaultNameProvider sets the [NameProvider] as a package-level default. +// +// By default, the default provider is [jsonname.DefaultJSONNameProvider]. +// +// It is safe to call concurrently with [Pointer.Get], [Pointer.Set], [GetForToken] and +// [SetForToken]. +// The typical usage is to call it once at initialization time. +// +// A nil provider is ignored. +func SetDefaultNameProvider(provider NameProvider) { + if provider == nil { + return + } + + defaultOptionsMu.Lock() + defer defaultOptionsMu.Unlock() + + defaultOptions.provider = provider +} + +// UseGoNameProvider sets the [NameProvider] as a package-level default to the alternative provider +// [jsonname.GoNameProvider], that covers a few areas not supported by the default name provider. +// +// This implementation supports untagged exported fields and embedded types in go struct. +// It follows strictly the behavior of the JSON standard library regarding field naming conventions. +// +// It is safe to call concurrently with [Pointer.Get], [Pointer.Set], [GetForToken] and +// [SetForToken]. +// The typical usage is to call it once at initialization time. +func UseGoNameProvider() { + SetDefaultNameProvider(jsonname.NewGoNameProvider()) +} + +// DefaultNameProvider returns the current package-level [NameProvider]. +func DefaultNameProvider() NameProvider { //nolint:ireturn // returning the interface is the point — callers pick their own implementation. + defaultOptionsMu.RLock() + defer defaultOptionsMu.RUnlock() + + return defaultOptions.provider +} + +// WithNameProvider injects a custom [NameProvider] to resolve json names from go struct types. +func WithNameProvider(provider NameProvider) Option { + return func(o *options) { + o.provider = provider + } +} + +type options struct { + provider NameProvider +} + +func optionsWithDefaults(opts []Option) options { + var o options + o.provider = DefaultNameProvider() + + for _, apply := range opts { + apply(&o) + } + + return o +} diff --git a/vendor/github.com/go-openapi/jsonpointer/pointer.go b/vendor/github.com/go-openapi/jsonpointer/pointer.go index d970c7cf4..05fc863ee 100644 --- a/vendor/github.com/go-openapi/jsonpointer/pointer.go +++ b/vendor/github.com/go-openapi/jsonpointer/pointer.go @@ -1,28 +1,7 @@ -// Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// author sigu-399 -// author-github https://github.com/sigu-399 -// author-mail sigu.399@gmail.com -// -// repository-name jsonpointer -// repository-desc An implementation of JSON Pointer - Go language -// -// description Main and unique file. -// -// created 25-02-2013 +// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 +// Package jsonpointer provides a golang implementation for json pointers. package jsonpointer import ( @@ -32,421 +11,670 @@ import ( "reflect" "strconv" "strings" - - "github.com/go-openapi/swag" ) const ( emptyPointer = `` pointerSeparator = `/` - - invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator - notFound = `Can't find the pointer in the document` ) -var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem() -var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem() - -// JSONPointable is an interface for structs to implement when they need to customize the -// json pointer process -type JSONPointable interface { - JSONLookup(string) (any, error) -} - -// JSONSetable is an interface for structs to implement when they need to customize the -// json pointer process -type JSONSetable interface { - JSONSet(string, any) error +// Pointer is a representation of a json pointer. +// +// Use [Pointer.Get] to retrieve a value or [Pointer.Set] to set a value. +// +// It works with any go type interpreted as a JSON document, which means: +// +// - if a type implements [JSONPointable], its [JSONPointable.JSONLookup] method is used to resolve [Pointer.Get] +// - if a type implements [JSONSetable], its [JSONSetable.JSONSet] method is used to resolve [Pointer.Set] +// - a go map[K]V is interpreted as an object, with type K assignable to a string +// - a go slice []T is interpreted as an array +// - a go struct is interpreted as an object, with exported fields interpreted as keys +// - promoted fields from an embedded struct are traversed +// - scalars (e.g. int, float64 ...), channels, functions and go arrays cannot be traversed +// +// For struct s resolved by reflection, key mappings honor the conventional struct tag `json`. +// +// Fields that do not specify a `json` tag, or specify an empty one, or are tagged as `json:"-"` are +// ignored. +// +// # Limitations +// +// - Unlike go standard marshaling, untagged fields do not default to the go field name and are ignored. +// - anonymous fields are not traversed if untagged +type Pointer struct { + referenceTokens []string } -// New creates a new json pointer for the given string +// New creates a new json pointer from its string representation. func New(jsonPointerString string) (Pointer, error) { - var p Pointer err := p.parse(jsonPointerString) - return p, err + return p, err } -// Pointer the json pointer reprsentation -type Pointer struct { - referenceTokens []string +// Get uses the pointer to retrieve a value from a JSON document. +// +// It returns the value with its type as a [reflect.Kind] or an error. +func (p *Pointer) Get(document any, opts ...Option) (any, reflect.Kind, error) { + o := optionsWithDefaults(opts) + + return p.get(document, o.provider) } -// "Constructor", parses the given string JSON pointer -func (p *Pointer) parse(jsonPointerString string) error { +// Set uses the pointer to set a value from a data type that represent a JSON document. +// +// # Mutation contract +// +// Set mutates the provided document in place whenever Go's type system allows it: when document is +// a map, a pointer, or when the targeted value is reached through an addressable ancestor (e.g. a +// struct field traversed via a pointer, a slice element). +// +// Callers that rely on this in-place behavior may continue to ignore the returned document. +// +// The returned document is only load-bearing when Set cannot mutate in place. +// +// This happens in one specific case: appending to a top-level slice passed by value (e.g. document +// of type []T rather than *[]T) via the RFC 6901 "-" terminal token. reflect.Append produces a new +// slice header that the library cannot rebind into the caller's variable; the updated document is +// returned instead. +// +// Pass *[]T if you want in-place rebind for that case as well. +// +// See [ErrDashToken] for the semantics of the "-" token. +func (p *Pointer) Set(document any, value any, opts ...Option) (any, error) { + o := optionsWithDefaults(opts) - var err error + return p.set(document, value, o.provider) +} - if jsonPointerString != emptyPointer { - if !strings.HasPrefix(jsonPointerString, pointerSeparator) { - err = errors.New(invalidStart) - } else { - referenceTokens := strings.Split(jsonPointerString, pointerSeparator) - p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) - } +// DecodedTokens returns the decoded (unescaped) tokens of this JSON pointer. +func (p *Pointer) DecodedTokens() []string { + result := make([]string, 0, len(p.referenceTokens)) + for _, token := range p.referenceTokens { + result = append(result, Unescape(token)) } - return err + return result +} + +// IsEmpty returns true if this is an empty json pointer. +// +// This indicates that it points to the root document. +func (p *Pointer) IsEmpty() bool { + return len(p.referenceTokens) == 0 } -// Get uses the pointer to retrieve a value from a JSON document -func (p *Pointer) Get(document any) (any, reflect.Kind, error) { - return p.get(document, swag.DefaultJSONNameProvider) +// String representation of a pointer. +func (p *Pointer) String() string { + if len(p.referenceTokens) == 0 { + return emptyPointer + } + + return pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator) } -// Set uses the pointer to set a value from a JSON document -func (p *Pointer) Set(document any, value any) (any, error) { - return document, p.set(document, value, swag.DefaultJSONNameProvider) +// Offset returns the byte offset, in the raw JSON text of document, of the location referenced by +// this pointer's terminal token. +// +// Unlike [Pointer.Get] and [Pointer.Set], which operate on a decoded Go value, Offset operates +// directly on the textual JSON source. +// +// It drives an [encoding/json.Decoder] over the string and stops at the terminal token, returning +// the position at which the decoder was about to read that token. +// +// It is primarily intended for tooling that needs to map a pointer back to a region of the original +// source: reporting line/column for validation or parse diagnostics, extracting a sub-document by +// slicing the raw bytes, or highlighting the referenced span in an editor. +// +// # Offset semantics +// +// The meaning of the returned offset depends on whether the terminal token addresses an object +// property or an array element: +// +// - Object property: the offset points to the first byte of the key (its +// opening quote character), not to the associated value. For example, +// pointer "/foo/bar" against {"foo": {"bar": 21}} returns 9, the index of +// the opening quote of "bar". +// - Array element: the offset points to the first byte of the value at that +// index. For example, pointer "/0/1" against [[1,2], [3,4]] returns 4, +// the index of the digit 2. +// +// # Errors +// +// Offset returns an error in any of these cases: +// +// - document is not syntactically valid JSON; +// - the structure of document does not match the pointer (e.g. traversing +// into a scalar, or a token that is neither a valid key nor a valid +// numeric index); +// - a referenced key or index does not exist in document; +// - the pointer's terminal token is the RFC 6901 "-" array token, which +// designates a nonexistent element and therefore has no offset in the +// source. The returned error wraps [ErrDashToken]. +// +// All errors wrap [ErrPointer]. +func (p *Pointer) Offset(document string) (int64, error) { + dec := json.NewDecoder(strings.NewReader(document)) + var offset int64 + for _, ttk := range p.DecodedTokens() { + tk, err := dec.Token() + if err != nil { + return 0, err + } + switch tk := tk.(type) { + case json.Delim: + switch tk { + case '{': + offset, err = offsetSingleObject(dec, ttk) + if err != nil { + return 0, err + } + case '[': + offset, err = offsetSingleArray(dec, ttk) + if err != nil { + return 0, err + } + default: + return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer) + } + default: + return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer) + } + } + return skipJSONSeparator(document, offset), nil } -// GetForToken gets a value for a json pointer token 1 level deep -func GetForToken(document any, decodedToken string) (any, reflect.Kind, error) { - return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider) +// skipJSONSeparator advances offset past trailing JSON whitespace and at most one value separator +// (comma) in document, so the result points at the first byte of the next JSON token. +// +// The streaming decoder's InputOffset sits right after the most recently consumed token, which +// between values is the comma (or whitespace) — not the following token. +// +// Normalizing here keeps Offset's contract uniform: for both object keys and array elements, and +// regardless of position within the parent container, the returned offset always points at the +// first byte of the addressed token. +func skipJSONSeparator(document string, offset int64) int64 { + n := int64(len(document)) + for offset < n && isJSONWhitespace(document[offset]) { + offset++ + } + if offset < n && document[offset] == ',' { + offset++ + } + for offset < n && isJSONWhitespace(document[offset]) { + offset++ + } + return offset } -// SetForToken gets a value for a json pointer token 1 level deep -func SetForToken(document any, decodedToken string, value any) (any, error) { - return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider) +func isJSONWhitespace(c byte) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' } -func isNil(input any) bool { - if input == nil { - return true +// "Constructor", parses the given string JSON pointer. +func (p *Pointer) parse(jsonPointerString string) error { + if jsonPointerString == emptyPointer { + return nil } - kind := reflect.TypeOf(input).Kind() - switch kind { //nolint:exhaustive - case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan: - return reflect.ValueOf(input).IsNil() - default: - return false + if !strings.HasPrefix(jsonPointerString, pointerSeparator) { + // non empty pointer must start with "/" + return errors.Join(ErrInvalidStart, ErrPointer) } + + referenceTokens := strings.Split(jsonPointerString, pointerSeparator) + p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) + + return nil } -func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) { - rValue := reflect.Indirect(reflect.ValueOf(node)) - kind := rValue.Kind() - if isNil(node) { - return nil, kind, fmt.Errorf("nil value has not field %q", decodedToken) +func (p *Pointer) get(node any, nameProvider NameProvider) (any, reflect.Kind, error) { + if nameProvider == nil { + nameProvider = defaultOptions.provider } - switch typed := node.(type) { - case JSONPointable: - r, err := typed.JSONLookup(decodedToken) + kind := reflect.Invalid + + // full document when empty + if len(p.referenceTokens) == 0 { + return node, kind, nil + } + + for _, token := range p.referenceTokens { + decodedToken := Unescape(token) + + r, knd, err := getSingleImpl(node, decodedToken, nameProvider) if err != nil { - return nil, kind, err + return nil, knd, err } - return r, kind, nil - case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect - return getSingleImpl(*typed, decodedToken, nameProvider) + node = r + } + + rValue := reflect.ValueOf(node) + kind = rValue.Kind() + + return node, kind, nil +} + +func (p *Pointer) set(node, data any, nameProvider NameProvider) (any, error) { + knd := reflect.ValueOf(node).Kind() + + if knd != reflect.Pointer && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array { + return node, errors.Join( + fmt.Errorf("unexpected type: %T", node), //nolint:err113 // err wrapping is carried out by errors.Join, not fmt.Errorf. + ErrUnsupportedValueType, + ErrPointer, + ) + } + + // full document when empty + if len(p.referenceTokens) == 0 { + return node, nil + } + + if nameProvider == nil { + nameProvider = defaultOptions.provider + } + + return p.setAt(node, p.referenceTokens, data, nameProvider) +} + +// setAt recursively walks the token list, setting the data at the terminal token and rebinding any +// new child reference (e.g. a slice header returned by an "-" append) into its parent on the way +// back up. +// +// Returning the (possibly new) node at each level is what makes append work at any depth without +// requiring the caller to pass a pointer to the containing slice: the new slice header propagates +// up and each parent rebinds it via the appropriate kind-specific setter. +func (p *Pointer) setAt(node any, tokens []string, data any, nameProvider NameProvider) (any, error) { + decodedToken := Unescape(tokens[0]) + + if len(tokens) == 1 { + return setSingleImpl(node, data, decodedToken, nameProvider) } - switch kind { //nolint:exhaustive + child, err := p.resolveNodeForToken(node, decodedToken, nameProvider) + if err != nil { + return node, err + } + + newChild, err := p.setAt(child, tokens[1:], data, nameProvider) + if err != nil { + return node, err + } + + return rebindChild(node, decodedToken, newChild, nameProvider) +} + +// rebindChild writes newChild back into node at decodedToken. +// +// For cases where the child was already mutated in place (pointer aliasing, addressable slice +// elements) the rebind is a safe no-op. +// +// For cases where the child was returned by value (map entries holding a slice, slices reached +// through a non-addressable ancestor), the rebind propagates the new value into the parent. +// +// Parents implementing [JSONPointable] are left alone: they took ownership of the child via +// JSONLookup and did not opt into a JSONSet-based rebind on intermediate tokens. +func rebindChild(node any, decodedToken string, newChild any, nameProvider NameProvider) (any, error) { + if _, ok := node.(JSONPointable); ok { + return node, nil + } + + rValue := reflect.Indirect(reflect.ValueOf(node)) + + switch rValue.Kind() { case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { - return nil, kind, fmt.Errorf("object has no field %q", decodedToken) + return node, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) } fld := rValue.FieldByName(nm) - return fld.Interface(), kind, nil + if !fld.CanSet() { + return node, nil + } + assignReflectValue(fld, newChild) + return node, nil case reflect.Map: - kv := reflect.ValueOf(decodedToken) - mv := rValue.MapIndex(kv) - - if mv.IsValid() { - return mv.Interface(), kind, nil - } - return nil, kind, fmt.Errorf("object has no key %q", decodedToken) + rValue.SetMapIndex(reflect.ValueOf(decodedToken), reflect.ValueOf(newChild)) + return node, nil case reflect.Slice: - tokenIndex, err := strconv.Atoi(decodedToken) + if decodedToken == dashToken { + return node, errDashIntermediate() + } + idx, err := strconv.Atoi(decodedToken) if err != nil { - return nil, kind, err + return node, errors.Join(err, ErrPointer) } - sLength := rValue.Len() - if tokenIndex < 0 || tokenIndex >= sLength { - return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex) + elem := rValue.Index(idx) + if !elem.CanSet() { + return node, nil } - - elem := rValue.Index(tokenIndex) - return elem.Interface(), kind, nil + assignReflectValue(elem, newChild) + return node, nil default: - return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken) + return node, errInvalidReference(decodedToken) } - } -func setSingleImpl(node, data any, decodedToken string, nameProvider *swag.NameProvider) error { - rValue := reflect.Indirect(reflect.ValueOf(node)) +// assignReflectValue assigns src into dst, unwrapping a pointer when dst expects the pointee type. +// +// This tolerates the pointer-wrapping performed by [typeFromValue] for addressable fields. +func assignReflectValue(dst reflect.Value, src any) { + nv := reflect.ValueOf(src) + if !nv.IsValid() { + return + } + if nv.Type().AssignableTo(dst.Type()) { + dst.Set(nv) + return + } + if nv.Kind() == reflect.Pointer && nv.Elem().Type().AssignableTo(dst.Type()) { + dst.Set(nv.Elem()) + } +} - if ns, ok := node.(JSONSetable); ok { // pointer impl - return ns.JSONSet(decodedToken, data) +func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvider NameProvider) (next any, err error) { + // check for nil during traversal + if isNil(node) { + return nil, fmt.Errorf("cannot traverse through nil value at %q: %w", decodedToken, ErrPointer) } - if rValue.Type().Implements(jsonSetableType) { - return node.(JSONSetable).JSONSet(decodedToken, data) + pointable, ok := node.(JSONPointable) + if ok { + r, err := pointable.JSONLookup(decodedToken) + if err != nil { + return nil, err + } + + fld := reflect.ValueOf(r) + if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Pointer { + return fld.Addr().Interface(), nil + } + + return r, nil } - switch rValue.Kind() { //nolint:exhaustive + rValue := reflect.Indirect(reflect.ValueOf(node)) + kind := rValue.Kind() + + switch kind { case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { - return fmt.Errorf("object has no field %q", decodedToken) - } - fld := rValue.FieldByName(nm) - if fld.IsValid() { - fld.Set(reflect.ValueOf(data)) + return nil, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) } - return nil + + return typeFromValue(rValue.FieldByName(nm)), nil case reflect.Map: kv := reflect.ValueOf(decodedToken) - rValue.SetMapIndex(kv, reflect.ValueOf(data)) - return nil + mv := rValue.MapIndex(kv) + + if !mv.IsValid() { + return nil, errNoKey(decodedToken) + } + + return typeFromValue(mv), nil case reflect.Slice: + if decodedToken == dashToken { + return nil, errDashIntermediate() + } tokenIndex, err := strconv.Atoi(decodedToken) if err != nil { - return err + return nil, errors.Join(err, ErrPointer) } + sLength := rValue.Len() if tokenIndex < 0 || tokenIndex >= sLength { - return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex) + return nil, errOutOfBounds(sLength, tokenIndex) } - elem := rValue.Index(tokenIndex) - if !elem.CanSet() { - return fmt.Errorf("can't set slice index %s to %v", decodedToken, data) - } - elem.Set(reflect.ValueOf(data)) - return nil + return typeFromValue(rValue.Index(tokenIndex)), nil default: - return fmt.Errorf("invalid token reference %q", decodedToken) + return nil, errInvalidReference(decodedToken) } - } -func (p *Pointer) get(node any, nameProvider *swag.NameProvider) (any, reflect.Kind, error) { - - if nameProvider == nil { - nameProvider = swag.DefaultJSONNameProvider +func isNil(input any) bool { + if input == nil { + return true } - kind := reflect.Invalid + kind := reflect.TypeOf(input).Kind() + switch kind { + case reflect.Pointer, reflect.Map, reflect.Slice, reflect.Chan: + return reflect.ValueOf(input).IsNil() + default: + return false + } +} - // Full document when empty - if len(p.referenceTokens) == 0 { - return node, kind, nil +func typeFromValue(v reflect.Value) any { + if v.CanAddr() && v.Kind() != reflect.Interface && v.Kind() != reflect.Map && v.Kind() != reflect.Slice && v.Kind() != reflect.Pointer { + return v.Addr().Interface() } - for _, token := range p.referenceTokens { + return v.Interface() +} - decodedToken := Unescape(token) +// GetForToken gets a value for a json pointer token 1 level deep. +func GetForToken(document any, decodedToken string, opts ...Option) (any, reflect.Kind, error) { + o := optionsWithDefaults(opts) - r, knd, err := getSingleImpl(node, decodedToken, nameProvider) + return getSingleImpl(document, decodedToken, o.provider) +} + +// SetForToken sets a value for a json pointer token 1 level deep. +// +// See [Pointer.Set] for the mutation contract, in particular the handling of the RFC 6901 "-" token +// on slices. +func SetForToken(document any, decodedToken string, value any, opts ...Option) (any, error) { + o := optionsWithDefaults(opts) + + return setSingleImpl(document, value, decodedToken, o.provider) +} + +func getSingleImpl(node any, decodedToken string, nameProvider NameProvider) (any, reflect.Kind, error) { + rValue := reflect.Indirect(reflect.ValueOf(node)) + kind := rValue.Kind() + if isNil(node) { + return nil, kind, fmt.Errorf("nil value has no field %q: %w", decodedToken, ErrPointer) + } + + switch typed := node.(type) { + case JSONPointable: + r, err := typed.JSONLookup(decodedToken) if err != nil { - return nil, knd, err + return nil, kind, err } - node = r + return r, kind, nil + case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect + return getSingleImpl(*typed, decodedToken, nameProvider) } - rValue := reflect.ValueOf(node) - kind = rValue.Kind() + switch kind { + case reflect.Struct: + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return nil, kind, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) + } - return node, kind, nil -} + fld := rValue.FieldByName(nm) -func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error { - knd := reflect.ValueOf(node).Kind() + return fld.Interface(), kind, nil - if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array { - return errors.New("only structs, pointers, maps and slices are supported for setting values") + case reflect.Map: + kv := reflect.ValueOf(decodedToken) + mv := rValue.MapIndex(kv) + + if mv.IsValid() { + return mv.Interface(), kind, nil + } + + return nil, kind, errNoKey(decodedToken) + + case reflect.Slice: + if decodedToken == dashToken { + return nil, kind, errDashOnGet() + } + tokenIndex, err := strconv.Atoi(decodedToken) + if err != nil { + return nil, kind, errors.Join(err, ErrPointer) + } + sLength := rValue.Len() + if tokenIndex < 0 || tokenIndex >= sLength { + return nil, kind, errOutOfBounds(sLength, tokenIndex) + } + + elem := rValue.Index(tokenIndex) + return elem.Interface(), kind, nil + + default: + return nil, kind, errInvalidReference(decodedToken) } +} - if nameProvider == nil { - nameProvider = swag.DefaultJSONNameProvider +func setSingleImpl(node, data any, decodedToken string, nameProvider NameProvider) (any, error) { + // check for nil to prevent panic when calling rValue.Type() + if isNil(node) { + return node, fmt.Errorf("cannot set field %q on nil value: %w", decodedToken, ErrPointer) } - // Full document when empty - if len(p.referenceTokens) == 0 { - return nil + if ns, ok := node.(JSONSetable); ok { + return node, ns.JSONSet(decodedToken, data) } - lastI := len(p.referenceTokens) - 1 - for i, token := range p.referenceTokens { - isLastToken := i == lastI - decodedToken := Unescape(token) + rValue := reflect.Indirect(reflect.ValueOf(node)) - if isLastToken { + switch rValue.Kind() { + case reflect.Struct: + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return node, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) + } - return setSingleImpl(node, data, decodedToken, nameProvider) + fld := rValue.FieldByName(nm) + if !fld.CanSet() { + return node, fmt.Errorf("can't set struct field %s to %v: %w", nm, data, ErrPointer) } - rValue := reflect.Indirect(reflect.ValueOf(node)) - kind := rValue.Kind() + value := reflect.ValueOf(data) + valueType := value.Type() + assignedType := fld.Type() - if rValue.Type().Implements(jsonPointableType) { - r, err := node.(JSONPointable).JSONLookup(decodedToken) - if err != nil { - return err - } - fld := reflect.ValueOf(r) - if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr { - node = fld.Addr().Interface() - continue - } - node = r - continue + if !valueType.AssignableTo(assignedType) { + return node, fmt.Errorf("can't set value with type %T to field %s with type %v: %w", data, nm, assignedType, ErrPointer) } - switch kind { //nolint:exhaustive - case reflect.Struct: - nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) - if !ok { - return fmt.Errorf("object has no field %q", decodedToken) - } - fld := rValue.FieldByName(nm) - if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr { - node = fld.Addr().Interface() - continue - } - node = fld.Interface() + fld.Set(value) - case reflect.Map: - kv := reflect.ValueOf(decodedToken) - mv := rValue.MapIndex(kv) + return node, nil - if !mv.IsValid() { - return fmt.Errorf("object has no key %q", decodedToken) - } - if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr { - node = mv.Addr().Interface() - continue - } - node = mv.Interface() + case reflect.Map: + kv := reflect.ValueOf(decodedToken) + rValue.SetMapIndex(kv, reflect.ValueOf(data)) - case reflect.Slice: - tokenIndex, err := strconv.Atoi(decodedToken) - if err != nil { - return err + return node, nil + + case reflect.Slice: + if decodedToken == dashToken { + // RFC 6901 §4 / RFC 6902 append semantics: terminal "-" appends the value to the slice. + // + // We rebind in place when the slice is reachable via an addressable ancestor; otherwise we + // return the new slice header for the parent (or the public Set) to rebind. + value := reflect.ValueOf(data) + elemType := rValue.Type().Elem() + if !value.Type().AssignableTo(elemType) { + return node, fmt.Errorf("can't append value of type %T to slice of %v: %w", data, elemType, ErrPointer) } - sLength := rValue.Len() - if tokenIndex < 0 || tokenIndex >= sLength { - return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex) + newSlice := reflect.Append(rValue, value) + if rValue.CanSet() { + rValue.Set(newSlice) + return node, nil } + return newSlice.Interface(), nil + } - elem := rValue.Index(tokenIndex) - if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr { - node = elem.Addr().Interface() - continue - } - node = elem.Interface() + tokenIndex, err := strconv.Atoi(decodedToken) + if err != nil { + return node, errors.Join(err, ErrPointer) + } - default: - return fmt.Errorf("invalid token reference %q", decodedToken) + sLength := rValue.Len() + if tokenIndex < 0 || tokenIndex >= sLength { + return node, errOutOfBounds(sLength, tokenIndex) } - } + elem := rValue.Index(tokenIndex) + if !elem.CanSet() { + return node, fmt.Errorf("can't set slice index %s to %v: %w", decodedToken, data, ErrPointer) + } - return nil -} + value := reflect.ValueOf(data) + valueType := value.Type() + assignedType := elem.Type() -// DecodedTokens returns the decoded tokens -func (p *Pointer) DecodedTokens() []string { - result := make([]string, 0, len(p.referenceTokens)) - for _, t := range p.referenceTokens { - result = append(result, Unescape(t)) - } - return result -} + if !valueType.AssignableTo(assignedType) { + return node, fmt.Errorf("can't set value with type %T to slice element %d with type %v: %w", data, tokenIndex, assignedType, ErrPointer) + } -// IsEmpty returns true if this is an empty json pointer -// this indicates that it points to the root document -func (p *Pointer) IsEmpty() bool { - return len(p.referenceTokens) == 0 -} + elem.Set(value) -// Pointer to string representation function -func (p *Pointer) String() string { + return node, nil - if len(p.referenceTokens) == 0 { - return emptyPointer + default: + return node, errInvalidReference(decodedToken) } - - pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator) - - return pointerString } -func (p *Pointer) Offset(document string) (int64, error) { - dec := json.NewDecoder(strings.NewReader(document)) - var offset int64 - for _, ttk := range p.DecodedTokens() { +func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { + for dec.More() { + offset := dec.InputOffset() tk, err := dec.Token() if err != nil { return 0, err } - switch tk := tk.(type) { - case json.Delim: - switch tk { - case '{': - offset, err = offsetSingleObject(dec, ttk) - if err != nil { - return 0, err - } - case '[': - offset, err = offsetSingleArray(dec, ttk) - if err != nil { - return 0, err - } - default: - return 0, fmt.Errorf("invalid token %#v", tk) - } - default: - return 0, fmt.Errorf("invalid token %#v", tk) + key, ok := tk.(string) + if !ok { + return 0, fmt.Errorf("invalid key token %#v: %w", tk, ErrPointer) + } + if key == decodedToken { + return offset, nil } - } - return offset, nil -} -func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { - for dec.More() { - offset := dec.InputOffset() - tk, err := dec.Token() + // Consume the associated value. + // Scalars are fully read by a single Token() call; composite values must be drained. + tk, err = dec.Token() if err != nil { return 0, err } - switch tk := tk.(type) { - case json.Delim: - switch tk { - case '{': - if err = drainSingle(dec); err != nil { - return 0, err - } - case '[': + if delim, isDelim := tk.(json.Delim); isDelim { + switch delim { + case '{', '[': if err = drainSingle(dec); err != nil { return 0, err } } - case string: - if tk == decodedToken { - return offset, nil - } - default: - return 0, fmt.Errorf("invalid token %#v", tk) } } - return 0, fmt.Errorf("token reference %q not found", decodedToken) + + return 0, fmt.Errorf("token reference %q not found: %w", decodedToken, ErrPointer) } func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { + if decodedToken == dashToken { + return 0, errDashOnOffset() + } idx, err := strconv.Atoi(decodedToken) if err != nil { - return 0, fmt.Errorf("token reference %q is not a number: %v", decodedToken, err) + return 0, fmt.Errorf("token reference %q is not a number: %w: %w", decodedToken, err, ErrPointer) } var i int for i = 0; i < idx && dec.More(); i++ { @@ -470,12 +698,14 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { } if !dec.More() { - return 0, fmt.Errorf("token reference %q not found", decodedToken) + return 0, fmt.Errorf("token reference %q not found: %w", decodedToken, ErrPointer) } + return dec.InputOffset(), nil } // drainSingle drains a single level of object or array. +// // The decoder has to guarantee the beginning delim (i.e. '{' or '[') has been consumed. func drainSingle(dec *json.Decoder) error { for dec.More() { @@ -497,17 +727,15 @@ func drainSingle(dec *json.Decoder) error { } } - // Consumes the ending delim + // consumes the ending delim if _, err := dec.Token(); err != nil { return err } + return nil } -// Specific JSON pointer encoding here -// ~0 => ~ -// ~1 => / -// ... and vice versa +// JSON pointer encoding: ~0 => ~ ~1 => / ... and vice versa. const ( encRefTok0 = `~0` @@ -516,16 +744,24 @@ const ( decRefTok1 = `/` ) -// Unescape unescapes a json pointer reference token string to the original representation +var ( + encRefTokReplacer = strings.NewReplacer(encRefTok1, decRefTok1, encRefTok0, decRefTok0) //nolint:gochecknoglobals // it's okay to declare a replacer as a private global + decRefTokReplacer = strings.NewReplacer(decRefTok1, encRefTok1, decRefTok0, encRefTok0) //nolint:gochecknoglobals // it's okay to declare a replacer as a private global +) + +// Unescape unescapes a json pointer reference token string to the original representation. func Unescape(token string) string { - step1 := strings.ReplaceAll(token, encRefTok1, decRefTok1) - step2 := strings.ReplaceAll(step1, encRefTok0, decRefTok0) - return step2 + return encRefTokReplacer.Replace(token) } -// Escape escapes a pointer reference token string +// Escape escapes a pointer reference token string. +// +// The JSONPointer specification defines "/" as a separator and "~" as an escape prefix. +// +// Keys containing such characters are escaped with the following rules: +// +// - "~" is escaped as "~0" +// - "/" is escaped as "~1" func Escape(token string) string { - step1 := strings.ReplaceAll(token, decRefTok0, encRefTok0) - step2 := strings.ReplaceAll(step1, decRefTok1, encRefTok1) - return step2 + return decRefTokReplacer.Replace(token) } diff --git a/vendor/github.com/go-openapi/swag/.editorconfig b/vendor/github.com/go-openapi/swag/.editorconfig deleted file mode 100644 index 3152da69a..000000000 --- a/vendor/github.com/go-openapi/swag/.editorconfig +++ /dev/null @@ -1,26 +0,0 @@ -# top-most EditorConfig file -root = true - -# Unix-style newlines with a newline ending every file -[*] -end_of_line = lf -insert_final_newline = true -indent_style = space -indent_size = 2 -trim_trailing_whitespace = true - -# Set default charset -[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] -charset = utf-8 - -# Tab indentation (no size specified) -[*.go] -indent_style = tab - -[*.md] -trim_trailing_whitespace = false - -# Matches the exact files either package.json or .travis.yml -[{package.json,.travis.yml}] -indent_style = space -indent_size = 2 diff --git a/vendor/github.com/go-openapi/swag/.gitattributes b/vendor/github.com/go-openapi/swag/.gitattributes deleted file mode 100644 index 49ad52766..000000000 --- a/vendor/github.com/go-openapi/swag/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# gofmt always uses LF, whereas Git uses CRLF on Windows. -*.go text eol=lf diff --git a/vendor/github.com/go-openapi/swag/.gitignore b/vendor/github.com/go-openapi/swag/.gitignore deleted file mode 100644 index c4b1b64f0..000000000 --- a/vendor/github.com/go-openapi/swag/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -secrets.yml -vendor -Godeps -.idea -*.out diff --git a/vendor/github.com/go-openapi/swag/.golangci.yml b/vendor/github.com/go-openapi/swag/.golangci.yml deleted file mode 100644 index 80e2be004..000000000 --- a/vendor/github.com/go-openapi/swag/.golangci.yml +++ /dev/null @@ -1,60 +0,0 @@ -linters-settings: - govet: - check-shadowing: true - golint: - min-confidence: 0 - gocyclo: - min-complexity: 45 - maligned: - suggest-new: true - dupl: - threshold: 200 - goconst: - min-len: 3 - min-occurrences: 3 - -linters: - enable-all: true - disable: - - maligned - - lll - - gochecknoinits - - gochecknoglobals - - funlen - - godox - - gocognit - - whitespace - - wsl - - wrapcheck - - testpackage - - nlreturn - - gomnd - - exhaustivestruct - - goerr113 - - errorlint - - nestif - - godot - - gofumpt - - paralleltest - - tparallel - - thelper - - ifshort - - exhaustruct - - varnamelen - - gci - - depguard - - errchkjson - - inamedparam - - nonamedreturns - - musttag - - ireturn - - forcetypeassert - - cyclop - # deprecated linters - - deadcode - - interfacer - - scopelint - - varcheck - - structcheck - - golint - - nosnakecase diff --git a/vendor/github.com/go-openapi/swag/BENCHMARK.md b/vendor/github.com/go-openapi/swag/BENCHMARK.md deleted file mode 100644 index e7f28ed6b..000000000 --- a/vendor/github.com/go-openapi/swag/BENCHMARK.md +++ /dev/null @@ -1,52 +0,0 @@ -# Benchmarks - -## Name mangling utilities - -```bash -go test -bench XXX -run XXX -benchtime 30s -``` - -### Benchmarks at b3e7a5386f996177e4808f11acb2aa93a0f660df - -``` -goos: linux -goarch: amd64 -pkg: github.com/go-openapi/swag -cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz -BenchmarkToXXXName/ToGoName-4 862623 44101 ns/op 10450 B/op 732 allocs/op -BenchmarkToXXXName/ToVarName-4 853656 40728 ns/op 10468 B/op 734 allocs/op -BenchmarkToXXXName/ToFileName-4 1268312 27813 ns/op 9785 B/op 617 allocs/op -BenchmarkToXXXName/ToCommandName-4 1276322 27903 ns/op 9785 B/op 617 allocs/op -BenchmarkToXXXName/ToHumanNameLower-4 895334 40354 ns/op 10472 B/op 731 allocs/op -BenchmarkToXXXName/ToHumanNameTitle-4 882441 40678 ns/op 10566 B/op 749 allocs/op -``` - -### Benchmarks after PR #79 - -~ x10 performance improvement and ~ /100 memory allocations. - -``` -goos: linux -goarch: amd64 -pkg: github.com/go-openapi/swag -cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz -BenchmarkToXXXName/ToGoName-4 9595830 3991 ns/op 42 B/op 5 allocs/op -BenchmarkToXXXName/ToVarName-4 9194276 3984 ns/op 62 B/op 7 allocs/op -BenchmarkToXXXName/ToFileName-4 17002711 2123 ns/op 147 B/op 7 allocs/op -BenchmarkToXXXName/ToCommandName-4 16772926 2111 ns/op 147 B/op 7 allocs/op -BenchmarkToXXXName/ToHumanNameLower-4 9788331 3749 ns/op 92 B/op 6 allocs/op -BenchmarkToXXXName/ToHumanNameTitle-4 9188260 3941 ns/op 104 B/op 6 allocs/op -``` - -``` -goos: linux -goarch: amd64 -pkg: github.com/go-openapi/swag -cpu: AMD Ryzen 7 5800X 8-Core Processor -BenchmarkToXXXName/ToGoName-16 18527378 1972 ns/op 42 B/op 5 allocs/op -BenchmarkToXXXName/ToVarName-16 15552692 2093 ns/op 62 B/op 7 allocs/op -BenchmarkToXXXName/ToFileName-16 32161176 1117 ns/op 147 B/op 7 allocs/op -BenchmarkToXXXName/ToCommandName-16 32256634 1137 ns/op 147 B/op 7 allocs/op -BenchmarkToXXXName/ToHumanNameLower-16 18599661 1946 ns/op 92 B/op 6 allocs/op -BenchmarkToXXXName/ToHumanNameTitle-16 17581353 2054 ns/op 105 B/op 6 allocs/op -``` diff --git a/vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md deleted file mode 100644 index 9322b065e..000000000 --- a/vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,74 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at ivan+abuse@flanders.co.nz. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/swag/LICENSE b/vendor/github.com/go-openapi/swag/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/vendor/github.com/go-openapi/swag/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/go-openapi/swag/README.md b/vendor/github.com/go-openapi/swag/README.md deleted file mode 100644 index a72922299..000000000 --- a/vendor/github.com/go-openapi/swag/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Swag [![Build Status](https://github.com/go-openapi/swag/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/swag/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/swag/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/swag) - -[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) -[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE) -[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/swag.svg)](https://pkg.go.dev/github.com/go-openapi/swag) -[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/swag)](https://goreportcard.com/report/github.com/go-openapi/swag) - -Contains a bunch of helper functions for go-openapi and go-swagger projects. - -You may also use it standalone for your projects. - -* convert between value and pointers for builtin types -* convert from string to builtin types (wraps strconv) -* fast json concatenation -* search in path -* load from file or http -* name mangling - - -This repo has only few dependencies outside of the standard library: - -* YAML utilities depend on `gopkg.in/yaml.v3` -* `github.com/mailru/easyjson v0.7.7` diff --git a/vendor/github.com/go-openapi/swag/convert.go b/vendor/github.com/go-openapi/swag/convert.go deleted file mode 100644 index fc085aeb8..000000000 --- a/vendor/github.com/go-openapi/swag/convert.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "math" - "strconv" - "strings" -) - -// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER -const ( - maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1 - minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1 - epsilon float64 = 1e-9 -) - -// IsFloat64AJSONInteger allow for integers [-2^53, 2^53-1] inclusive -func IsFloat64AJSONInteger(f float64) bool { - if math.IsNaN(f) || math.IsInf(f, 0) || f < minJSONFloat || f > maxJSONFloat { - return false - } - fa := math.Abs(f) - g := float64(uint64(f)) - ga := math.Abs(g) - - diff := math.Abs(f - g) - - // more info: https://floating-point-gui.de/errors/comparison/#look-out-for-edge-cases - switch { - case f == g: // best case - return true - case f == float64(int64(f)) || f == float64(uint64(f)): // optimistic case - return true - case f == 0 || g == 0 || diff < math.SmallestNonzeroFloat64: // very close to 0 values - return diff < (epsilon * math.SmallestNonzeroFloat64) - } - // check the relative error - return diff/math.Min(fa+ga, math.MaxFloat64) < epsilon -} - -var evaluatesAsTrue map[string]struct{} - -func init() { - evaluatesAsTrue = map[string]struct{}{ - "true": {}, - "1": {}, - "yes": {}, - "ok": {}, - "y": {}, - "on": {}, - "selected": {}, - "checked": {}, - "t": {}, - "enabled": {}, - } -} - -// ConvertBool turn a string into a boolean -func ConvertBool(str string) (bool, error) { - _, ok := evaluatesAsTrue[strings.ToLower(str)] - return ok, nil -} - -// ConvertFloat32 turn a string into a float32 -func ConvertFloat32(str string) (float32, error) { - f, err := strconv.ParseFloat(str, 32) - if err != nil { - return 0, err - } - return float32(f), nil -} - -// ConvertFloat64 turn a string into a float64 -func ConvertFloat64(str string) (float64, error) { - return strconv.ParseFloat(str, 64) -} - -// ConvertInt8 turn a string into an int8 -func ConvertInt8(str string) (int8, error) { - i, err := strconv.ParseInt(str, 10, 8) - if err != nil { - return 0, err - } - return int8(i), nil -} - -// ConvertInt16 turn a string into an int16 -func ConvertInt16(str string) (int16, error) { - i, err := strconv.ParseInt(str, 10, 16) - if err != nil { - return 0, err - } - return int16(i), nil -} - -// ConvertInt32 turn a string into an int32 -func ConvertInt32(str string) (int32, error) { - i, err := strconv.ParseInt(str, 10, 32) - if err != nil { - return 0, err - } - return int32(i), nil -} - -// ConvertInt64 turn a string into an int64 -func ConvertInt64(str string) (int64, error) { - return strconv.ParseInt(str, 10, 64) -} - -// ConvertUint8 turn a string into an uint8 -func ConvertUint8(str string) (uint8, error) { - i, err := strconv.ParseUint(str, 10, 8) - if err != nil { - return 0, err - } - return uint8(i), nil -} - -// ConvertUint16 turn a string into an uint16 -func ConvertUint16(str string) (uint16, error) { - i, err := strconv.ParseUint(str, 10, 16) - if err != nil { - return 0, err - } - return uint16(i), nil -} - -// ConvertUint32 turn a string into an uint32 -func ConvertUint32(str string) (uint32, error) { - i, err := strconv.ParseUint(str, 10, 32) - if err != nil { - return 0, err - } - return uint32(i), nil -} - -// ConvertUint64 turn a string into an uint64 -func ConvertUint64(str string) (uint64, error) { - return strconv.ParseUint(str, 10, 64) -} - -// FormatBool turns a boolean into a string -func FormatBool(value bool) string { - return strconv.FormatBool(value) -} - -// FormatFloat32 turns a float32 into a string -func FormatFloat32(value float32) string { - return strconv.FormatFloat(float64(value), 'f', -1, 32) -} - -// FormatFloat64 turns a float64 into a string -func FormatFloat64(value float64) string { - return strconv.FormatFloat(value, 'f', -1, 64) -} - -// FormatInt8 turns an int8 into a string -func FormatInt8(value int8) string { - return strconv.FormatInt(int64(value), 10) -} - -// FormatInt16 turns an int16 into a string -func FormatInt16(value int16) string { - return strconv.FormatInt(int64(value), 10) -} - -// FormatInt32 turns an int32 into a string -func FormatInt32(value int32) string { - return strconv.Itoa(int(value)) -} - -// FormatInt64 turns an int64 into a string -func FormatInt64(value int64) string { - return strconv.FormatInt(value, 10) -} - -// FormatUint8 turns an uint8 into a string -func FormatUint8(value uint8) string { - return strconv.FormatUint(uint64(value), 10) -} - -// FormatUint16 turns an uint16 into a string -func FormatUint16(value uint16) string { - return strconv.FormatUint(uint64(value), 10) -} - -// FormatUint32 turns an uint32 into a string -func FormatUint32(value uint32) string { - return strconv.FormatUint(uint64(value), 10) -} - -// FormatUint64 turns an uint64 into a string -func FormatUint64(value uint64) string { - return strconv.FormatUint(value, 10) -} diff --git a/vendor/github.com/go-openapi/swag/convert_types.go b/vendor/github.com/go-openapi/swag/convert_types.go deleted file mode 100644 index c49cc473a..000000000 --- a/vendor/github.com/go-openapi/swag/convert_types.go +++ /dev/null @@ -1,730 +0,0 @@ -package swag - -import "time" - -// This file was taken from the aws go sdk - -// String returns a pointer to of the string value passed in. -func String(v string) *string { - return &v -} - -// StringValue returns the value of the string pointer passed in or -// "" if the pointer is nil. -func StringValue(v *string) string { - if v != nil { - return *v - } - return "" -} - -// StringSlice converts a slice of string values into a slice of -// string pointers -func StringSlice(src []string) []*string { - dst := make([]*string, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// StringValueSlice converts a slice of string pointers into a slice of -// string values -func StringValueSlice(src []*string) []string { - dst := make([]string, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// StringMap converts a string map of string values into a string -// map of string pointers -func StringMap(src map[string]string) map[string]*string { - dst := make(map[string]*string) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// StringValueMap converts a string map of string pointers into a string -// map of string values -func StringValueMap(src map[string]*string) map[string]string { - dst := make(map[string]string) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} - -// Bool returns a pointer to of the bool value passed in. -func Bool(v bool) *bool { - return &v -} - -// BoolValue returns the value of the bool pointer passed in or -// false if the pointer is nil. -func BoolValue(v *bool) bool { - if v != nil { - return *v - } - return false -} - -// BoolSlice converts a slice of bool values into a slice of -// bool pointers -func BoolSlice(src []bool) []*bool { - dst := make([]*bool, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// BoolValueSlice converts a slice of bool pointers into a slice of -// bool values -func BoolValueSlice(src []*bool) []bool { - dst := make([]bool, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// BoolMap converts a string map of bool values into a string -// map of bool pointers -func BoolMap(src map[string]bool) map[string]*bool { - dst := make(map[string]*bool) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// BoolValueMap converts a string map of bool pointers into a string -// map of bool values -func BoolValueMap(src map[string]*bool) map[string]bool { - dst := make(map[string]bool) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} - -// Int returns a pointer to of the int value passed in. -func Int(v int) *int { - return &v -} - -// IntValue returns the value of the int pointer passed in or -// 0 if the pointer is nil. -func IntValue(v *int) int { - if v != nil { - return *v - } - return 0 -} - -// IntSlice converts a slice of int values into a slice of -// int pointers -func IntSlice(src []int) []*int { - dst := make([]*int, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// IntValueSlice converts a slice of int pointers into a slice of -// int values -func IntValueSlice(src []*int) []int { - dst := make([]int, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// IntMap converts a string map of int values into a string -// map of int pointers -func IntMap(src map[string]int) map[string]*int { - dst := make(map[string]*int) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// IntValueMap converts a string map of int pointers into a string -// map of int values -func IntValueMap(src map[string]*int) map[string]int { - dst := make(map[string]int) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} - -// Int32 returns a pointer to of the int32 value passed in. -func Int32(v int32) *int32 { - return &v -} - -// Int32Value returns the value of the int32 pointer passed in or -// 0 if the pointer is nil. -func Int32Value(v *int32) int32 { - if v != nil { - return *v - } - return 0 -} - -// Int32Slice converts a slice of int32 values into a slice of -// int32 pointers -func Int32Slice(src []int32) []*int32 { - dst := make([]*int32, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// Int32ValueSlice converts a slice of int32 pointers into a slice of -// int32 values -func Int32ValueSlice(src []*int32) []int32 { - dst := make([]int32, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// Int32Map converts a string map of int32 values into a string -// map of int32 pointers -func Int32Map(src map[string]int32) map[string]*int32 { - dst := make(map[string]*int32) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// Int32ValueMap converts a string map of int32 pointers into a string -// map of int32 values -func Int32ValueMap(src map[string]*int32) map[string]int32 { - dst := make(map[string]int32) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} - -// Int64 returns a pointer to of the int64 value passed in. -func Int64(v int64) *int64 { - return &v -} - -// Int64Value returns the value of the int64 pointer passed in or -// 0 if the pointer is nil. -func Int64Value(v *int64) int64 { - if v != nil { - return *v - } - return 0 -} - -// Int64Slice converts a slice of int64 values into a slice of -// int64 pointers -func Int64Slice(src []int64) []*int64 { - dst := make([]*int64, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// Int64ValueSlice converts a slice of int64 pointers into a slice of -// int64 values -func Int64ValueSlice(src []*int64) []int64 { - dst := make([]int64, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// Int64Map converts a string map of int64 values into a string -// map of int64 pointers -func Int64Map(src map[string]int64) map[string]*int64 { - dst := make(map[string]*int64) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// Int64ValueMap converts a string map of int64 pointers into a string -// map of int64 values -func Int64ValueMap(src map[string]*int64) map[string]int64 { - dst := make(map[string]int64) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} - -// Uint16 returns a pointer to of the uint16 value passed in. -func Uint16(v uint16) *uint16 { - return &v -} - -// Uint16Value returns the value of the uint16 pointer passed in or -// 0 if the pointer is nil. -func Uint16Value(v *uint16) uint16 { - if v != nil { - return *v - } - - return 0 -} - -// Uint16Slice converts a slice of uint16 values into a slice of -// uint16 pointers -func Uint16Slice(src []uint16) []*uint16 { - dst := make([]*uint16, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - - return dst -} - -// Uint16ValueSlice converts a slice of uint16 pointers into a slice of -// uint16 values -func Uint16ValueSlice(src []*uint16) []uint16 { - dst := make([]uint16, len(src)) - - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - - return dst -} - -// Uint16Map converts a string map of uint16 values into a string -// map of uint16 pointers -func Uint16Map(src map[string]uint16) map[string]*uint16 { - dst := make(map[string]*uint16) - - for k, val := range src { - v := val - dst[k] = &v - } - - return dst -} - -// Uint16ValueMap converts a string map of uint16 pointers into a string -// map of uint16 values -func Uint16ValueMap(src map[string]*uint16) map[string]uint16 { - dst := make(map[string]uint16) - - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - - return dst -} - -// Uint returns a pointer to of the uint value passed in. -func Uint(v uint) *uint { - return &v -} - -// UintValue returns the value of the uint pointer passed in or -// 0 if the pointer is nil. -func UintValue(v *uint) uint { - if v != nil { - return *v - } - return 0 -} - -// UintSlice converts a slice of uint values into a slice of -// uint pointers -func UintSlice(src []uint) []*uint { - dst := make([]*uint, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// UintValueSlice converts a slice of uint pointers into a slice of -// uint values -func UintValueSlice(src []*uint) []uint { - dst := make([]uint, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// UintMap converts a string map of uint values into a string -// map of uint pointers -func UintMap(src map[string]uint) map[string]*uint { - dst := make(map[string]*uint) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// UintValueMap converts a string map of uint pointers into a string -// map of uint values -func UintValueMap(src map[string]*uint) map[string]uint { - dst := make(map[string]uint) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} - -// Uint32 returns a pointer to of the uint32 value passed in. -func Uint32(v uint32) *uint32 { - return &v -} - -// Uint32Value returns the value of the uint32 pointer passed in or -// 0 if the pointer is nil. -func Uint32Value(v *uint32) uint32 { - if v != nil { - return *v - } - return 0 -} - -// Uint32Slice converts a slice of uint32 values into a slice of -// uint32 pointers -func Uint32Slice(src []uint32) []*uint32 { - dst := make([]*uint32, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// Uint32ValueSlice converts a slice of uint32 pointers into a slice of -// uint32 values -func Uint32ValueSlice(src []*uint32) []uint32 { - dst := make([]uint32, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// Uint32Map converts a string map of uint32 values into a string -// map of uint32 pointers -func Uint32Map(src map[string]uint32) map[string]*uint32 { - dst := make(map[string]*uint32) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// Uint32ValueMap converts a string map of uint32 pointers into a string -// map of uint32 values -func Uint32ValueMap(src map[string]*uint32) map[string]uint32 { - dst := make(map[string]uint32) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} - -// Uint64 returns a pointer to of the uint64 value passed in. -func Uint64(v uint64) *uint64 { - return &v -} - -// Uint64Value returns the value of the uint64 pointer passed in or -// 0 if the pointer is nil. -func Uint64Value(v *uint64) uint64 { - if v != nil { - return *v - } - return 0 -} - -// Uint64Slice converts a slice of uint64 values into a slice of -// uint64 pointers -func Uint64Slice(src []uint64) []*uint64 { - dst := make([]*uint64, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// Uint64ValueSlice converts a slice of uint64 pointers into a slice of -// uint64 values -func Uint64ValueSlice(src []*uint64) []uint64 { - dst := make([]uint64, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// Uint64Map converts a string map of uint64 values into a string -// map of uint64 pointers -func Uint64Map(src map[string]uint64) map[string]*uint64 { - dst := make(map[string]*uint64) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// Uint64ValueMap converts a string map of uint64 pointers into a string -// map of uint64 values -func Uint64ValueMap(src map[string]*uint64) map[string]uint64 { - dst := make(map[string]uint64) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} - -// Float32 returns a pointer to of the float32 value passed in. -func Float32(v float32) *float32 { - return &v -} - -// Float32Value returns the value of the float32 pointer passed in or -// 0 if the pointer is nil. -func Float32Value(v *float32) float32 { - if v != nil { - return *v - } - - return 0 -} - -// Float32Slice converts a slice of float32 values into a slice of -// float32 pointers -func Float32Slice(src []float32) []*float32 { - dst := make([]*float32, len(src)) - - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - - return dst -} - -// Float32ValueSlice converts a slice of float32 pointers into a slice of -// float32 values -func Float32ValueSlice(src []*float32) []float32 { - dst := make([]float32, len(src)) - - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - - return dst -} - -// Float32Map converts a string map of float32 values into a string -// map of float32 pointers -func Float32Map(src map[string]float32) map[string]*float32 { - dst := make(map[string]*float32) - - for k, val := range src { - v := val - dst[k] = &v - } - - return dst -} - -// Float32ValueMap converts a string map of float32 pointers into a string -// map of float32 values -func Float32ValueMap(src map[string]*float32) map[string]float32 { - dst := make(map[string]float32) - - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - - return dst -} - -// Float64 returns a pointer to of the float64 value passed in. -func Float64(v float64) *float64 { - return &v -} - -// Float64Value returns the value of the float64 pointer passed in or -// 0 if the pointer is nil. -func Float64Value(v *float64) float64 { - if v != nil { - return *v - } - return 0 -} - -// Float64Slice converts a slice of float64 values into a slice of -// float64 pointers -func Float64Slice(src []float64) []*float64 { - dst := make([]*float64, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// Float64ValueSlice converts a slice of float64 pointers into a slice of -// float64 values -func Float64ValueSlice(src []*float64) []float64 { - dst := make([]float64, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// Float64Map converts a string map of float64 values into a string -// map of float64 pointers -func Float64Map(src map[string]float64) map[string]*float64 { - dst := make(map[string]*float64) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// Float64ValueMap converts a string map of float64 pointers into a string -// map of float64 values -func Float64ValueMap(src map[string]*float64) map[string]float64 { - dst := make(map[string]float64) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} - -// Time returns a pointer to of the time.Time value passed in. -func Time(v time.Time) *time.Time { - return &v -} - -// TimeValue returns the value of the time.Time pointer passed in or -// time.Time{} if the pointer is nil. -func TimeValue(v *time.Time) time.Time { - if v != nil { - return *v - } - return time.Time{} -} - -// TimeSlice converts a slice of time.Time values into a slice of -// time.Time pointers -func TimeSlice(src []time.Time) []*time.Time { - dst := make([]*time.Time, len(src)) - for i := 0; i < len(src); i++ { - dst[i] = &(src[i]) - } - return dst -} - -// TimeValueSlice converts a slice of time.Time pointers into a slice of -// time.Time values -func TimeValueSlice(src []*time.Time) []time.Time { - dst := make([]time.Time, len(src)) - for i := 0; i < len(src); i++ { - if src[i] != nil { - dst[i] = *(src[i]) - } - } - return dst -} - -// TimeMap converts a string map of time.Time values into a string -// map of time.Time pointers -func TimeMap(src map[string]time.Time) map[string]*time.Time { - dst := make(map[string]*time.Time) - for k, val := range src { - v := val - dst[k] = &v - } - return dst -} - -// TimeValueMap converts a string map of time.Time pointers into a string -// map of time.Time values -func TimeValueMap(src map[string]*time.Time) map[string]time.Time { - dst := make(map[string]time.Time) - for k, val := range src { - if val != nil { - dst[k] = *val - } - } - return dst -} diff --git a/vendor/github.com/go-openapi/swag/doc.go b/vendor/github.com/go-openapi/swag/doc.go deleted file mode 100644 index 55094cb74..000000000 --- a/vendor/github.com/go-openapi/swag/doc.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package swag contains a bunch of helper functions for go-openapi and go-swagger projects. - -You may also use it standalone for your projects. - - - convert between value and pointers for builtin types - - convert from string to builtin types (wraps strconv) - - fast json concatenation - - search in path - - load from file or http - - name mangling - -This repo has only few dependencies outside of the standard library: - - - YAML utilities depend on gopkg.in/yaml.v2 -*/ -package swag diff --git a/vendor/github.com/go-openapi/swag/file.go b/vendor/github.com/go-openapi/swag/file.go deleted file mode 100644 index 16accc55f..000000000 --- a/vendor/github.com/go-openapi/swag/file.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import "mime/multipart" - -// File represents an uploaded file. -type File struct { - Data multipart.File - Header *multipart.FileHeader -} - -// Read bytes from the file -func (f *File) Read(p []byte) (n int, err error) { - return f.Data.Read(p) -} - -// Close the file -func (f *File) Close() error { - return f.Data.Close() -} diff --git a/vendor/github.com/go-openapi/swag/initialism_index.go b/vendor/github.com/go-openapi/swag/initialism_index.go deleted file mode 100644 index 20a359bb6..000000000 --- a/vendor/github.com/go-openapi/swag/initialism_index.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "sort" - "strings" - "sync" -) - -var ( - // commonInitialisms are common acronyms that are kept as whole uppercased words. - commonInitialisms *indexOfInitialisms - - // initialisms is a slice of sorted initialisms - initialisms []string - - // a copy of initialisms pre-baked as []rune - initialismsRunes [][]rune - initialismsUpperCased [][]rune - - isInitialism func(string) bool - - maxAllocMatches int -) - -func init() { - // Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769 - configuredInitialisms := map[string]bool{ - "ACL": true, - "API": true, - "ASCII": true, - "CPU": true, - "CSS": true, - "DNS": true, - "EOF": true, - "GUID": true, - "HTML": true, - "HTTPS": true, - "HTTP": true, - "ID": true, - "IP": true, - "IPv4": true, - "IPv6": true, - "JSON": true, - "LHS": true, - "OAI": true, - "QPS": true, - "RAM": true, - "RHS": true, - "RPC": true, - "SLA": true, - "SMTP": true, - "SQL": true, - "SSH": true, - "TCP": true, - "TLS": true, - "TTL": true, - "UDP": true, - "UI": true, - "UID": true, - "UUID": true, - "URI": true, - "URL": true, - "UTF8": true, - "VM": true, - "XML": true, - "XMPP": true, - "XSRF": true, - "XSS": true, - } - - // a thread-safe index of initialisms - commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms) - initialisms = commonInitialisms.sorted() - initialismsRunes = asRunes(initialisms) - initialismsUpperCased = asUpperCased(initialisms) - maxAllocMatches = maxAllocHeuristic(initialismsRunes) - - // a test function - isInitialism = commonInitialisms.isInitialism -} - -func asRunes(in []string) [][]rune { - out := make([][]rune, len(in)) - for i, initialism := range in { - out[i] = []rune(initialism) - } - - return out -} - -func asUpperCased(in []string) [][]rune { - out := make([][]rune, len(in)) - - for i, initialism := range in { - out[i] = []rune(upper(trim(initialism))) - } - - return out -} - -func maxAllocHeuristic(in [][]rune) int { - heuristic := make(map[rune]int) - for _, initialism := range in { - heuristic[initialism[0]]++ - } - - var maxAlloc int - for _, val := range heuristic { - if val > maxAlloc { - maxAlloc = val - } - } - - return maxAlloc -} - -// AddInitialisms add additional initialisms -func AddInitialisms(words ...string) { - for _, word := range words { - // commonInitialisms[upper(word)] = true - commonInitialisms.add(upper(word)) - } - // sort again - initialisms = commonInitialisms.sorted() - initialismsRunes = asRunes(initialisms) - initialismsUpperCased = asUpperCased(initialisms) -} - -// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms. -// Since go1.9, this may be implemented with sync.Map. -type indexOfInitialisms struct { - sortMutex *sync.Mutex - index *sync.Map -} - -func newIndexOfInitialisms() *indexOfInitialisms { - return &indexOfInitialisms{ - sortMutex: new(sync.Mutex), - index: new(sync.Map), - } -} - -func (m *indexOfInitialisms) load(initial map[string]bool) *indexOfInitialisms { - m.sortMutex.Lock() - defer m.sortMutex.Unlock() - for k, v := range initial { - m.index.Store(k, v) - } - return m -} - -func (m *indexOfInitialisms) isInitialism(key string) bool { - _, ok := m.index.Load(key) - return ok -} - -func (m *indexOfInitialisms) add(key string) *indexOfInitialisms { - m.index.Store(key, true) - return m -} - -func (m *indexOfInitialisms) sorted() (result []string) { - m.sortMutex.Lock() - defer m.sortMutex.Unlock() - m.index.Range(func(key, _ interface{}) bool { - k := key.(string) - result = append(result, k) - return true - }) - sort.Sort(sort.Reverse(byInitialism(result))) - return -} - -type byInitialism []string - -func (s byInitialism) Len() int { - return len(s) -} -func (s byInitialism) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} -func (s byInitialism) Less(i, j int) bool { - if len(s[i]) != len(s[j]) { - return len(s[i]) < len(s[j]) - } - - return strings.Compare(s[i], s[j]) > 0 -} diff --git a/vendor/github.com/go-openapi/swag/json.go b/vendor/github.com/go-openapi/swag/json.go deleted file mode 100644 index 7e9902ca3..000000000 --- a/vendor/github.com/go-openapi/swag/json.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "bytes" - "encoding/json" - "log" - "reflect" - "strings" - "sync" - - "github.com/mailru/easyjson/jlexer" - "github.com/mailru/easyjson/jwriter" -) - -// nullJSON represents a JSON object with null type -var nullJSON = []byte("null") - -// DefaultJSONNameProvider the default cache for types -var DefaultJSONNameProvider = NewNameProvider() - -const comma = byte(',') - -var closers map[byte]byte - -func init() { - closers = map[byte]byte{ - '{': '}', - '[': ']', - } -} - -type ejMarshaler interface { - MarshalEasyJSON(w *jwriter.Writer) -} - -type ejUnmarshaler interface { - UnmarshalEasyJSON(w *jlexer.Lexer) -} - -// WriteJSON writes json data, prefers finding an appropriate interface to short-circuit the marshaler -// so it takes the fastest option available. -func WriteJSON(data interface{}) ([]byte, error) { - if d, ok := data.(ejMarshaler); ok { - jw := new(jwriter.Writer) - d.MarshalEasyJSON(jw) - return jw.BuildBytes() - } - if d, ok := data.(json.Marshaler); ok { - return d.MarshalJSON() - } - return json.Marshal(data) -} - -// ReadJSON reads json data, prefers finding an appropriate interface to short-circuit the unmarshaler -// so it takes the fastest option available -func ReadJSON(data []byte, value interface{}) error { - trimmedData := bytes.Trim(data, "\x00") - if d, ok := value.(ejUnmarshaler); ok { - jl := &jlexer.Lexer{Data: trimmedData} - d.UnmarshalEasyJSON(jl) - return jl.Error() - } - if d, ok := value.(json.Unmarshaler); ok { - return d.UnmarshalJSON(trimmedData) - } - return json.Unmarshal(trimmedData, value) -} - -// DynamicJSONToStruct converts an untyped json structure into a struct -func DynamicJSONToStruct(data interface{}, target interface{}) error { - // TODO: convert straight to a json typed map (mergo + iterate?) - b, err := WriteJSON(data) - if err != nil { - return err - } - return ReadJSON(b, target) -} - -// ConcatJSON concatenates multiple json objects efficiently -func ConcatJSON(blobs ...[]byte) []byte { - if len(blobs) == 0 { - return nil - } - - last := len(blobs) - 1 - for blobs[last] == nil || bytes.Equal(blobs[last], nullJSON) { - // strips trailing null objects - last-- - if last < 0 { - // there was nothing but "null"s or nil... - return nil - } - } - if last == 0 { - return blobs[0] - } - - var opening, closing byte - var idx, a int - buf := bytes.NewBuffer(nil) - - for i, b := range blobs[:last+1] { - if b == nil || bytes.Equal(b, nullJSON) { - // a null object is in the list: skip it - continue - } - if len(b) > 0 && opening == 0 { // is this an array or an object? - opening, closing = b[0], closers[b[0]] - } - - if opening != '{' && opening != '[' { - continue // don't know how to concatenate non container objects - } - - if len(b) < 3 { // yep empty but also the last one, so closing this thing - if i == last && a > 0 { - if err := buf.WriteByte(closing); err != nil { - log.Println(err) - } - } - continue - } - - idx = 0 - if a > 0 { // we need to join with a comma for everything beyond the first non-empty item - if err := buf.WriteByte(comma); err != nil { - log.Println(err) - } - idx = 1 // this is not the first or the last so we want to drop the leading bracket - } - - if i != last { // not the last one, strip brackets - if _, err := buf.Write(b[idx : len(b)-1]); err != nil { - log.Println(err) - } - } else { // last one, strip only the leading bracket - if _, err := buf.Write(b[idx:]); err != nil { - log.Println(err) - } - } - a++ - } - // somehow it ended up being empty, so provide a default value - if buf.Len() == 0 { - if err := buf.WriteByte(opening); err != nil { - log.Println(err) - } - if err := buf.WriteByte(closing); err != nil { - log.Println(err) - } - } - return buf.Bytes() -} - -// ToDynamicJSON turns an object into a properly JSON typed structure -func ToDynamicJSON(data interface{}) interface{} { - // TODO: convert straight to a json typed map (mergo + iterate?) - b, err := json.Marshal(data) - if err != nil { - log.Println(err) - } - var res interface{} - if err := json.Unmarshal(b, &res); err != nil { - log.Println(err) - } - return res -} - -// FromDynamicJSON turns an object into a properly JSON typed structure -func FromDynamicJSON(data, target interface{}) error { - b, err := json.Marshal(data) - if err != nil { - log.Println(err) - } - return json.Unmarshal(b, target) -} - -// NameProvider represents an object capable of translating from go property names -// to json property names -// This type is thread-safe. -type NameProvider struct { - lock *sync.Mutex - index map[reflect.Type]nameIndex -} - -type nameIndex struct { - jsonNames map[string]string - goNames map[string]string -} - -// NewNameProvider creates a new name provider -func NewNameProvider() *NameProvider { - return &NameProvider{ - lock: &sync.Mutex{}, - index: make(map[reflect.Type]nameIndex), - } -} - -func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) { - for i := 0; i < tpe.NumField(); i++ { - targetDes := tpe.Field(i) - - if targetDes.PkgPath != "" { // unexported - continue - } - - if targetDes.Anonymous { // walk embedded structures tree down first - buildnameIndex(targetDes.Type, idx, reverseIdx) - continue - } - - if tag := targetDes.Tag.Get("json"); tag != "" { - - parts := strings.Split(tag, ",") - if len(parts) == 0 { - continue - } - - nm := parts[0] - if nm == "-" { - continue - } - if nm == "" { // empty string means we want to use the Go name - nm = targetDes.Name - } - - idx[nm] = targetDes.Name - reverseIdx[targetDes.Name] = nm - } - } -} - -func newNameIndex(tpe reflect.Type) nameIndex { - var idx = make(map[string]string, tpe.NumField()) - var reverseIdx = make(map[string]string, tpe.NumField()) - - buildnameIndex(tpe, idx, reverseIdx) - return nameIndex{jsonNames: idx, goNames: reverseIdx} -} - -// GetJSONNames gets all the json property names for a type -func (n *NameProvider) GetJSONNames(subject interface{}) []string { - n.lock.Lock() - defer n.lock.Unlock() - tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() - names, ok := n.index[tpe] - if !ok { - names = n.makeNameIndex(tpe) - } - - res := make([]string, 0, len(names.jsonNames)) - for k := range names.jsonNames { - res = append(res, k) - } - return res -} - -// GetJSONName gets the json name for a go property name -func (n *NameProvider) GetJSONName(subject interface{}, name string) (string, bool) { - tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() - return n.GetJSONNameForType(tpe, name) -} - -// GetJSONNameForType gets the json name for a go property name on a given type -func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) { - n.lock.Lock() - defer n.lock.Unlock() - names, ok := n.index[tpe] - if !ok { - names = n.makeNameIndex(tpe) - } - nme, ok := names.goNames[name] - return nme, ok -} - -func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex { - names := newNameIndex(tpe) - n.index[tpe] = names - return names -} - -// GetGoName gets the go name for a json property name -func (n *NameProvider) GetGoName(subject interface{}, name string) (string, bool) { - tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() - return n.GetGoNameForType(tpe, name) -} - -// GetGoNameForType gets the go name for a given type for a json property name -func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) { - n.lock.Lock() - defer n.lock.Unlock() - names, ok := n.index[tpe] - if !ok { - names = n.makeNameIndex(tpe) - } - nme, ok := names.jsonNames[name] - return nme, ok -} diff --git a/vendor/github.com/go-openapi/swag/loading.go b/vendor/github.com/go-openapi/swag/loading.go deleted file mode 100644 index 783442fdd..000000000 --- a/vendor/github.com/go-openapi/swag/loading.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "fmt" - "io" - "log" - "net/http" - "net/url" - "os" - "path" - "path/filepath" - "runtime" - "strings" - "time" -) - -// LoadHTTPTimeout the default timeout for load requests -var LoadHTTPTimeout = 30 * time.Second - -// LoadHTTPBasicAuthUsername the username to use when load requests require basic auth -var LoadHTTPBasicAuthUsername = "" - -// LoadHTTPBasicAuthPassword the password to use when load requests require basic auth -var LoadHTTPBasicAuthPassword = "" - -// LoadHTTPCustomHeaders an optional collection of custom HTTP headers for load requests -var LoadHTTPCustomHeaders = map[string]string{} - -// LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in -func LoadFromFileOrHTTP(pth string) ([]byte, error) { - return LoadStrategy(pth, os.ReadFile, loadHTTPBytes(LoadHTTPTimeout))(pth) -} - -// LoadFromFileOrHTTPWithTimeout loads the bytes from a file or a remote http server based on the path passed in -// timeout arg allows for per request overriding of the request timeout -func LoadFromFileOrHTTPWithTimeout(pth string, timeout time.Duration) ([]byte, error) { - return LoadStrategy(pth, os.ReadFile, loadHTTPBytes(timeout))(pth) -} - -// LoadStrategy returns a loader function for a given path or URI. -// -// The load strategy returns the remote load for any path starting with `http`. -// So this works for any URI with a scheme `http` or `https`. -// -// The fallback strategy is to call the local loader. -// -// The local loader takes a local file system path (absolute or relative) as argument, -// or alternatively a `file://...` URI, **without host** (see also below for windows). -// -// There are a few liberalities, initially intended to be tolerant regarding the URI syntax, -// especially on windows. -// -// Before the local loader is called, the given path is transformed: -// - percent-encoded characters are unescaped -// - simple paths (e.g. `./folder/file`) are passed as-is -// - on windows, occurrences of `/` are replaced by `\`, so providing a relative path such a `folder/file` works too. -// -// For paths provided as URIs with the "file" scheme, please note that: -// - `file://` is simply stripped. -// This means that the host part of the URI is not parsed at all. -// For example, `file:///folder/file" becomes "/folder/file`, -// but `file://localhost/folder/file` becomes `localhost/folder/file` on unix systems. -// Similarly, `file://./folder/file` yields `./folder/file`. -// - on windows, `file://...` can take a host so as to specify an UNC share location. -// -// Reminder about windows-specifics: -// - `file://host/folder/file` becomes an UNC path like `\\host\folder\file` (no port specification is supported) -// - `file:///c:/folder/file` becomes `C:\folder\file` -// - `file://c:/folder/file` is tolerated (without leading `/`) and becomes `c:\folder\file` -func LoadStrategy(pth string, local, remote func(string) ([]byte, error)) func(string) ([]byte, error) { - if strings.HasPrefix(pth, "http") { - return remote - } - - return func(p string) ([]byte, error) { - upth, err := url.PathUnescape(p) - if err != nil { - return nil, err - } - - if !strings.HasPrefix(p, `file://`) { - // regular file path provided: just normalize slashes - return local(filepath.FromSlash(upth)) - } - - if runtime.GOOS != "windows" { - // crude processing: this leaves full URIs with a host with a (mostly) unexpected result - upth = strings.TrimPrefix(upth, `file://`) - - return local(filepath.FromSlash(upth)) - } - - // windows-only pre-processing of file://... URIs - - // support for canonical file URIs on windows. - u, err := url.Parse(filepath.ToSlash(upth)) - if err != nil { - return nil, err - } - - if u.Host != "" { - // assume UNC name (volume share) - // NOTE: UNC port not yet supported - - // when the "host" segment is a drive letter: - // file://C:/folder/... => C:\folder - upth = path.Clean(strings.Join([]string{u.Host, u.Path}, `/`)) - if !strings.HasSuffix(u.Host, ":") && u.Host[0] != '.' { - // tolerance: if we have a leading dot, this can't be a host - // file://host/share/folder\... ==> \\host\share\path\folder - upth = "//" + upth - } - } else { - // no host, let's figure out if this is a drive letter - upth = strings.TrimPrefix(upth, `file://`) - first, _, _ := strings.Cut(strings.TrimPrefix(u.Path, "/"), "/") - if strings.HasSuffix(first, ":") { - // drive letter in the first segment: - // file:///c:/folder/... ==> strip the leading slash - upth = strings.TrimPrefix(upth, `/`) - } - } - - return local(filepath.FromSlash(upth)) - } -} - -func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) { - return func(path string) ([]byte, error) { - client := &http.Client{Timeout: timeout} - req, err := http.NewRequest(http.MethodGet, path, nil) //nolint:noctx - if err != nil { - return nil, err - } - - if LoadHTTPBasicAuthUsername != "" && LoadHTTPBasicAuthPassword != "" { - req.SetBasicAuth(LoadHTTPBasicAuthUsername, LoadHTTPBasicAuthPassword) - } - - for key, val := range LoadHTTPCustomHeaders { - req.Header.Set(key, val) - } - - resp, err := client.Do(req) - defer func() { - if resp != nil { - if e := resp.Body.Close(); e != nil { - log.Println(e) - } - } - }() - if err != nil { - return nil, err - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("could not access document at %q [%s] ", path, resp.Status) - } - - return io.ReadAll(resp.Body) - } -} diff --git a/vendor/github.com/go-openapi/swag/name_lexem.go b/vendor/github.com/go-openapi/swag/name_lexem.go deleted file mode 100644 index 8bb64ac32..000000000 --- a/vendor/github.com/go-openapi/swag/name_lexem.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "unicode" - "unicode/utf8" -) - -type ( - lexemKind uint8 - - nameLexem struct { - original string - matchedInitialism string - kind lexemKind - } -) - -const ( - lexemKindCasualName lexemKind = iota - lexemKindInitialismName -) - -func newInitialismNameLexem(original, matchedInitialism string) nameLexem { - return nameLexem{ - kind: lexemKindInitialismName, - original: original, - matchedInitialism: matchedInitialism, - } -} - -func newCasualNameLexem(original string) nameLexem { - return nameLexem{ - kind: lexemKindCasualName, - original: original, - } -} - -func (l nameLexem) GetUnsafeGoName() string { - if l.kind == lexemKindInitialismName { - return l.matchedInitialism - } - - var ( - first rune - rest string - ) - - for i, orig := range l.original { - if i == 0 { - first = orig - continue - } - - if i > 0 { - rest = l.original[i:] - break - } - } - - if len(l.original) > 1 { - b := poolOfBuffers.BorrowBuffer(utf8.UTFMax + len(rest)) - defer func() { - poolOfBuffers.RedeemBuffer(b) - }() - b.WriteRune(unicode.ToUpper(first)) - b.WriteString(lower(rest)) - return b.String() - } - - return l.original -} - -func (l nameLexem) GetOriginal() string { - return l.original -} - -func (l nameLexem) IsInitialism() bool { - return l.kind == lexemKindInitialismName -} diff --git a/vendor/github.com/go-openapi/swag/net.go b/vendor/github.com/go-openapi/swag/net.go deleted file mode 100644 index 821235f84..000000000 --- a/vendor/github.com/go-openapi/swag/net.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "net" - "strconv" -) - -// SplitHostPort splits a network address into a host and a port. -// The port is -1 when there is no port to be found -func SplitHostPort(addr string) (host string, port int, err error) { - h, p, err := net.SplitHostPort(addr) - if err != nil { - return "", -1, err - } - if p == "" { - return "", -1, &net.AddrError{Err: "missing port in address", Addr: addr} - } - - pi, err := strconv.Atoi(p) - if err != nil { - return "", -1, err - } - return h, pi, nil -} diff --git a/vendor/github.com/go-openapi/swag/path.go b/vendor/github.com/go-openapi/swag/path.go deleted file mode 100644 index 941bd0176..000000000 --- a/vendor/github.com/go-openapi/swag/path.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "os" - "path/filepath" - "runtime" - "strings" -) - -const ( - // GOPATHKey represents the env key for gopath - GOPATHKey = "GOPATH" -) - -// FindInSearchPath finds a package in a provided lists of paths -func FindInSearchPath(searchPath, pkg string) string { - pathsList := filepath.SplitList(searchPath) - for _, path := range pathsList { - if evaluatedPath, err := filepath.EvalSymlinks(filepath.Join(path, "src", pkg)); err == nil { - if _, err := os.Stat(evaluatedPath); err == nil { - return evaluatedPath - } - } - } - return "" -} - -// FindInGoSearchPath finds a package in the $GOPATH:$GOROOT -func FindInGoSearchPath(pkg string) string { - return FindInSearchPath(FullGoSearchPath(), pkg) -} - -// FullGoSearchPath gets the search paths for finding packages -func FullGoSearchPath() string { - allPaths := os.Getenv(GOPATHKey) - if allPaths == "" { - allPaths = filepath.Join(os.Getenv("HOME"), "go") - } - if allPaths != "" { - allPaths = strings.Join([]string{allPaths, runtime.GOROOT()}, ":") - } else { - allPaths = runtime.GOROOT() - } - return allPaths -} diff --git a/vendor/github.com/go-openapi/swag/split.go b/vendor/github.com/go-openapi/swag/split.go deleted file mode 100644 index 274727a86..000000000 --- a/vendor/github.com/go-openapi/swag/split.go +++ /dev/null @@ -1,508 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "bytes" - "sync" - "unicode" - "unicode/utf8" -) - -type ( - splitter struct { - initialisms []string - initialismsRunes [][]rune - initialismsUpperCased [][]rune // initialisms cached in their trimmed, upper-cased version - postSplitInitialismCheck bool - } - - splitterOption func(*splitter) - - initialismMatch struct { - body []rune - start, end int - complete bool - } - initialismMatches []initialismMatch -) - -type ( - // memory pools of temporary objects. - // - // These are used to recycle temporarily allocated objects - // and relieve the GC from undue pressure. - - matchesPool struct { - *sync.Pool - } - - buffersPool struct { - *sync.Pool - } - - lexemsPool struct { - *sync.Pool - } - - splittersPool struct { - *sync.Pool - } -) - -var ( - // poolOfMatches holds temporary slices for recycling during the initialism match process - poolOfMatches = matchesPool{ - Pool: &sync.Pool{ - New: func() any { - s := make(initialismMatches, 0, maxAllocMatches) - - return &s - }, - }, - } - - poolOfBuffers = buffersPool{ - Pool: &sync.Pool{ - New: func() any { - return new(bytes.Buffer) - }, - }, - } - - poolOfLexems = lexemsPool{ - Pool: &sync.Pool{ - New: func() any { - s := make([]nameLexem, 0, maxAllocMatches) - - return &s - }, - }, - } - - poolOfSplitters = splittersPool{ - Pool: &sync.Pool{ - New: func() any { - s := newSplitter() - - return &s - }, - }, - } -) - -// nameReplaceTable finds a word representation for special characters. -func nameReplaceTable(r rune) (string, bool) { - switch r { - case '@': - return "At ", true - case '&': - return "And ", true - case '|': - return "Pipe ", true - case '$': - return "Dollar ", true - case '!': - return "Bang ", true - case '-': - return "", true - case '_': - return "", true - default: - return "", false - } -} - -// split calls the splitter. -// -// Use newSplitter for more control and options -func split(str string) []string { - s := poolOfSplitters.BorrowSplitter() - lexems := s.split(str) - result := make([]string, 0, len(*lexems)) - - for _, lexem := range *lexems { - result = append(result, lexem.GetOriginal()) - } - poolOfLexems.RedeemLexems(lexems) - poolOfSplitters.RedeemSplitter(s) - - return result - -} - -func newSplitter(options ...splitterOption) splitter { - s := splitter{ - postSplitInitialismCheck: false, - initialisms: initialisms, - initialismsRunes: initialismsRunes, - initialismsUpperCased: initialismsUpperCased, - } - - for _, option := range options { - option(&s) - } - - return s -} - -// withPostSplitInitialismCheck allows to catch initialisms after main split process -func withPostSplitInitialismCheck(s *splitter) { - s.postSplitInitialismCheck = true -} - -func (p matchesPool) BorrowMatches() *initialismMatches { - s := p.Get().(*initialismMatches) - *s = (*s)[:0] // reset slice, keep allocated capacity - - return s -} - -func (p buffersPool) BorrowBuffer(size int) *bytes.Buffer { - s := p.Get().(*bytes.Buffer) - s.Reset() - - if s.Cap() < size { - s.Grow(size) - } - - return s -} - -func (p lexemsPool) BorrowLexems() *[]nameLexem { - s := p.Get().(*[]nameLexem) - *s = (*s)[:0] // reset slice, keep allocated capacity - - return s -} - -func (p splittersPool) BorrowSplitter(options ...splitterOption) *splitter { - s := p.Get().(*splitter) - s.postSplitInitialismCheck = false // reset options - for _, apply := range options { - apply(s) - } - - return s -} - -func (p matchesPool) RedeemMatches(s *initialismMatches) { - p.Put(s) -} - -func (p buffersPool) RedeemBuffer(s *bytes.Buffer) { - p.Put(s) -} - -func (p lexemsPool) RedeemLexems(s *[]nameLexem) { - p.Put(s) -} - -func (p splittersPool) RedeemSplitter(s *splitter) { - p.Put(s) -} - -func (m initialismMatch) isZero() bool { - return m.start == 0 && m.end == 0 -} - -func (s splitter) split(name string) *[]nameLexem { - nameRunes := []rune(name) - matches := s.gatherInitialismMatches(nameRunes) - if matches == nil { - return poolOfLexems.BorrowLexems() - } - - return s.mapMatchesToNameLexems(nameRunes, matches) -} - -func (s splitter) gatherInitialismMatches(nameRunes []rune) *initialismMatches { - var matches *initialismMatches - - for currentRunePosition, currentRune := range nameRunes { - // recycle these allocations as we loop over runes - // with such recycling, only 2 slices should be allocated per call - // instead of o(n). - newMatches := poolOfMatches.BorrowMatches() - - // check current initialism matches - if matches != nil { // skip first iteration - for _, match := range *matches { - if keepCompleteMatch := match.complete; keepCompleteMatch { - *newMatches = append(*newMatches, match) - continue - } - - // drop failed match - currentMatchRune := match.body[currentRunePosition-match.start] - if currentMatchRune != currentRune { - continue - } - - // try to complete ongoing match - if currentRunePosition-match.start == len(match.body)-1 { - // we are close; the next step is to check the symbol ahead - // if it is a small letter, then it is not the end of match - // but beginning of the next word - - if currentRunePosition < len(nameRunes)-1 { - nextRune := nameRunes[currentRunePosition+1] - if newWord := unicode.IsLower(nextRune); newWord { - // oh ok, it was the start of a new word - continue - } - } - - match.complete = true - match.end = currentRunePosition - } - - *newMatches = append(*newMatches, match) - } - } - - // check for new initialism matches - for i := range s.initialisms { - initialismRunes := s.initialismsRunes[i] - if initialismRunes[0] == currentRune { - *newMatches = append(*newMatches, initialismMatch{ - start: currentRunePosition, - body: initialismRunes, - complete: false, - }) - } - } - - if matches != nil { - poolOfMatches.RedeemMatches(matches) - } - matches = newMatches - } - - // up to the caller to redeem this last slice - return matches -} - -func (s splitter) mapMatchesToNameLexems(nameRunes []rune, matches *initialismMatches) *[]nameLexem { - nameLexems := poolOfLexems.BorrowLexems() - - var lastAcceptedMatch initialismMatch - for _, match := range *matches { - if !match.complete { - continue - } - - if firstMatch := lastAcceptedMatch.isZero(); firstMatch { - s.appendBrokenDownCasualString(nameLexems, nameRunes[:match.start]) - *nameLexems = append(*nameLexems, s.breakInitialism(string(match.body))) - - lastAcceptedMatch = match - - continue - } - - if overlappedMatch := match.start <= lastAcceptedMatch.end; overlappedMatch { - continue - } - - middle := nameRunes[lastAcceptedMatch.end+1 : match.start] - s.appendBrokenDownCasualString(nameLexems, middle) - *nameLexems = append(*nameLexems, s.breakInitialism(string(match.body))) - - lastAcceptedMatch = match - } - - // we have not found any accepted matches - if lastAcceptedMatch.isZero() { - *nameLexems = (*nameLexems)[:0] - s.appendBrokenDownCasualString(nameLexems, nameRunes) - } else if lastAcceptedMatch.end+1 != len(nameRunes) { - rest := nameRunes[lastAcceptedMatch.end+1:] - s.appendBrokenDownCasualString(nameLexems, rest) - } - - poolOfMatches.RedeemMatches(matches) - - return nameLexems -} - -func (s splitter) breakInitialism(original string) nameLexem { - return newInitialismNameLexem(original, original) -} - -func (s splitter) appendBrokenDownCasualString(segments *[]nameLexem, str []rune) { - currentSegment := poolOfBuffers.BorrowBuffer(len(str)) // unlike strings.Builder, bytes.Buffer initial storage can reused - defer func() { - poolOfBuffers.RedeemBuffer(currentSegment) - }() - - addCasualNameLexem := func(original string) { - *segments = append(*segments, newCasualNameLexem(original)) - } - - addInitialismNameLexem := func(original, match string) { - *segments = append(*segments, newInitialismNameLexem(original, match)) - } - - var addNameLexem func(string) - if s.postSplitInitialismCheck { - addNameLexem = func(original string) { - for i := range s.initialisms { - if isEqualFoldIgnoreSpace(s.initialismsUpperCased[i], original) { - addInitialismNameLexem(original, s.initialisms[i]) - - return - } - } - - addCasualNameLexem(original) - } - } else { - addNameLexem = addCasualNameLexem - } - - for _, rn := range str { - if replace, found := nameReplaceTable(rn); found { - if currentSegment.Len() > 0 { - addNameLexem(currentSegment.String()) - currentSegment.Reset() - } - - if replace != "" { - addNameLexem(replace) - } - - continue - } - - if !unicode.In(rn, unicode.L, unicode.M, unicode.N, unicode.Pc) { - if currentSegment.Len() > 0 { - addNameLexem(currentSegment.String()) - currentSegment.Reset() - } - - continue - } - - if unicode.IsUpper(rn) { - if currentSegment.Len() > 0 { - addNameLexem(currentSegment.String()) - } - currentSegment.Reset() - } - - currentSegment.WriteRune(rn) - } - - if currentSegment.Len() > 0 { - addNameLexem(currentSegment.String()) - } -} - -// isEqualFoldIgnoreSpace is the same as strings.EqualFold, but -// it ignores leading and trailing blank spaces in the compared -// string. -// -// base is assumed to be composed of upper-cased runes, and be already -// trimmed. -// -// This code is heavily inspired from strings.EqualFold. -func isEqualFoldIgnoreSpace(base []rune, str string) bool { - var i, baseIndex int - // equivalent to b := []byte(str), but without data copy - b := hackStringBytes(str) - - for i < len(b) { - if c := b[i]; c < utf8.RuneSelf { - // fast path for ASCII - if c != ' ' && c != '\t' { - break - } - i++ - - continue - } - - // unicode case - r, size := utf8.DecodeRune(b[i:]) - if !unicode.IsSpace(r) { - break - } - i += size - } - - if i >= len(b) { - return len(base) == 0 - } - - for _, baseRune := range base { - if i >= len(b) { - break - } - - if c := b[i]; c < utf8.RuneSelf { - // single byte rune case (ASCII) - if baseRune >= utf8.RuneSelf { - return false - } - - baseChar := byte(baseRune) - if c != baseChar && - !('a' <= c && c <= 'z' && c-'a'+'A' == baseChar) { - return false - } - - baseIndex++ - i++ - - continue - } - - // unicode case - r, size := utf8.DecodeRune(b[i:]) - if unicode.ToUpper(r) != baseRune { - return false - } - baseIndex++ - i += size - } - - if baseIndex != len(base) { - return false - } - - // all passed: now we should only have blanks - for i < len(b) { - if c := b[i]; c < utf8.RuneSelf { - // fast path for ASCII - if c != ' ' && c != '\t' { - return false - } - i++ - - continue - } - - // unicode case - r, size := utf8.DecodeRune(b[i:]) - if !unicode.IsSpace(r) { - return false - } - - i += size - } - - return true -} diff --git a/vendor/github.com/go-openapi/swag/string_bytes.go b/vendor/github.com/go-openapi/swag/string_bytes.go deleted file mode 100644 index 90745d5ca..000000000 --- a/vendor/github.com/go-openapi/swag/string_bytes.go +++ /dev/null @@ -1,8 +0,0 @@ -package swag - -import "unsafe" - -// hackStringBytes returns the (unsafe) underlying bytes slice of a string. -func hackStringBytes(str string) []byte { - return unsafe.Slice(unsafe.StringData(str), len(str)) -} diff --git a/vendor/github.com/go-openapi/swag/util.go b/vendor/github.com/go-openapi/swag/util.go deleted file mode 100644 index 5051401c4..000000000 --- a/vendor/github.com/go-openapi/swag/util.go +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "reflect" - "strings" - "unicode" - "unicode/utf8" -) - -// GoNamePrefixFunc sets an optional rule to prefix go names -// which do not start with a letter. -// -// The prefix function is assumed to return a string that starts with an upper case letter. -// -// e.g. to help convert "123" into "{prefix}123" -// -// The default is to prefix with "X" -var GoNamePrefixFunc func(string) string - -func prefixFunc(name, in string) string { - if GoNamePrefixFunc == nil { - return "X" + in - } - - return GoNamePrefixFunc(name) + in -} - -const ( - // collectionFormatComma = "csv" - collectionFormatSpace = "ssv" - collectionFormatTab = "tsv" - collectionFormatPipe = "pipes" - collectionFormatMulti = "multi" -) - -// JoinByFormat joins a string array by a known format (e.g. swagger's collectionFormat attribute): -// -// ssv: space separated value -// tsv: tab separated value -// pipes: pipe (|) separated value -// csv: comma separated value (default) -func JoinByFormat(data []string, format string) []string { - if len(data) == 0 { - return data - } - var sep string - switch format { - case collectionFormatSpace: - sep = " " - case collectionFormatTab: - sep = "\t" - case collectionFormatPipe: - sep = "|" - case collectionFormatMulti: - return data - default: - sep = "," - } - return []string{strings.Join(data, sep)} -} - -// SplitByFormat splits a string by a known format: -// -// ssv: space separated value -// tsv: tab separated value -// pipes: pipe (|) separated value -// csv: comma separated value (default) -func SplitByFormat(data, format string) []string { - if data == "" { - return nil - } - var sep string - switch format { - case collectionFormatSpace: - sep = " " - case collectionFormatTab: - sep = "\t" - case collectionFormatPipe: - sep = "|" - case collectionFormatMulti: - return nil - default: - sep = "," - } - var result []string - for _, s := range strings.Split(data, sep) { - if ts := strings.TrimSpace(s); ts != "" { - result = append(result, ts) - } - } - return result -} - -// Removes leading whitespaces -func trim(str string) string { - return strings.TrimSpace(str) -} - -// Shortcut to strings.ToUpper() -func upper(str string) string { - return strings.ToUpper(trim(str)) -} - -// Shortcut to strings.ToLower() -func lower(str string) string { - return strings.ToLower(trim(str)) -} - -// Camelize an uppercased word -func Camelize(word string) string { - camelized := poolOfBuffers.BorrowBuffer(len(word)) - defer func() { - poolOfBuffers.RedeemBuffer(camelized) - }() - - for pos, ru := range []rune(word) { - if pos > 0 { - camelized.WriteRune(unicode.ToLower(ru)) - } else { - camelized.WriteRune(unicode.ToUpper(ru)) - } - } - return camelized.String() -} - -// ToFileName lowercases and underscores a go type name -func ToFileName(name string) string { - in := split(name) - out := make([]string, 0, len(in)) - - for _, w := range in { - out = append(out, lower(w)) - } - - return strings.Join(out, "_") -} - -// ToCommandName lowercases and underscores a go type name -func ToCommandName(name string) string { - in := split(name) - out := make([]string, 0, len(in)) - - for _, w := range in { - out = append(out, lower(w)) - } - return strings.Join(out, "-") -} - -// ToHumanNameLower represents a code name as a human series of words -func ToHumanNameLower(name string) string { - s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck) - in := s.split(name) - poolOfSplitters.RedeemSplitter(s) - out := make([]string, 0, len(*in)) - - for _, w := range *in { - if !w.IsInitialism() { - out = append(out, lower(w.GetOriginal())) - } else { - out = append(out, trim(w.GetOriginal())) - } - } - poolOfLexems.RedeemLexems(in) - - return strings.Join(out, " ") -} - -// ToHumanNameTitle represents a code name as a human series of words with the first letters titleized -func ToHumanNameTitle(name string) string { - s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck) - in := s.split(name) - poolOfSplitters.RedeemSplitter(s) - - out := make([]string, 0, len(*in)) - for _, w := range *in { - original := trim(w.GetOriginal()) - if !w.IsInitialism() { - out = append(out, Camelize(original)) - } else { - out = append(out, original) - } - } - poolOfLexems.RedeemLexems(in) - - return strings.Join(out, " ") -} - -// ToJSONName camelcases a name which can be underscored or pascal cased -func ToJSONName(name string) string { - in := split(name) - out := make([]string, 0, len(in)) - - for i, w := range in { - if i == 0 { - out = append(out, lower(w)) - continue - } - out = append(out, Camelize(trim(w))) - } - return strings.Join(out, "") -} - -// ToVarName camelcases a name which can be underscored or pascal cased -func ToVarName(name string) string { - res := ToGoName(name) - if isInitialism(res) { - return lower(res) - } - if len(res) <= 1 { - return lower(res) - } - return lower(res[:1]) + res[1:] -} - -// ToGoName translates a swagger name which can be underscored or camel cased to a name that golint likes -func ToGoName(name string) string { - s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck) - lexems := s.split(name) - poolOfSplitters.RedeemSplitter(s) - defer func() { - poolOfLexems.RedeemLexems(lexems) - }() - lexemes := *lexems - - if len(lexemes) == 0 { - return "" - } - - result := poolOfBuffers.BorrowBuffer(len(name)) - defer func() { - poolOfBuffers.RedeemBuffer(result) - }() - - // check if not starting with a letter, upper case - firstPart := lexemes[0].GetUnsafeGoName() - if lexemes[0].IsInitialism() { - firstPart = upper(firstPart) - } - - if c := firstPart[0]; c < utf8.RuneSelf { - // ASCII - switch { - case 'A' <= c && c <= 'Z': - result.WriteString(firstPart) - case 'a' <= c && c <= 'z': - result.WriteByte(c - 'a' + 'A') - result.WriteString(firstPart[1:]) - default: - result.WriteString(prefixFunc(name, firstPart)) - // NOTE: no longer check if prefixFunc returns a string that starts with uppercase: - // assume this is always the case - } - } else { - // unicode - firstRune, _ := utf8.DecodeRuneInString(firstPart) - switch { - case !unicode.IsLetter(firstRune): - result.WriteString(prefixFunc(name, firstPart)) - case !unicode.IsUpper(firstRune): - result.WriteString(prefixFunc(name, firstPart)) - /* - result.WriteRune(unicode.ToUpper(firstRune)) - result.WriteString(firstPart[offset:]) - */ - default: - result.WriteString(firstPart) - } - } - - for _, lexem := range lexemes[1:] { - goName := lexem.GetUnsafeGoName() - - // to support old behavior - if lexem.IsInitialism() { - goName = upper(goName) - } - result.WriteString(goName) - } - - return result.String() -} - -// ContainsStrings searches a slice of strings for a case-sensitive match -func ContainsStrings(coll []string, item string) bool { - for _, a := range coll { - if a == item { - return true - } - } - return false -} - -// ContainsStringsCI searches a slice of strings for a case-insensitive match -func ContainsStringsCI(coll []string, item string) bool { - for _, a := range coll { - if strings.EqualFold(a, item) { - return true - } - } - return false -} - -type zeroable interface { - IsZero() bool -} - -// IsZero returns true when the value passed into the function is a zero value. -// This allows for safer checking of interface values. -func IsZero(data interface{}) bool { - v := reflect.ValueOf(data) - // check for nil data - switch v.Kind() { //nolint:exhaustive - case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - if v.IsNil() { - return true - } - } - - // check for things that have an IsZero method instead - if vv, ok := data.(zeroable); ok { - return vv.IsZero() - } - - // continue with slightly more complex reflection - switch v.Kind() { //nolint:exhaustive - case reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Struct, reflect.Array: - return reflect.DeepEqual(data, reflect.Zero(v.Type()).Interface()) - case reflect.Invalid: - return true - default: - return false - } -} - -// CommandLineOptionsGroup represents a group of user-defined command line options -type CommandLineOptionsGroup struct { - ShortDescription string - LongDescription string - Options interface{} -} diff --git a/vendor/github.com/go-openapi/swag/yaml.go b/vendor/github.com/go-openapi/swag/yaml.go deleted file mode 100644 index f59e02593..000000000 --- a/vendor/github.com/go-openapi/swag/yaml.go +++ /dev/null @@ -1,481 +0,0 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package swag - -import ( - "encoding/json" - "errors" - "fmt" - "path/filepath" - "reflect" - "sort" - "strconv" - - "github.com/mailru/easyjson/jlexer" - "github.com/mailru/easyjson/jwriter" - yaml "gopkg.in/yaml.v3" -) - -// YAMLMatcher matches yaml -func YAMLMatcher(path string) bool { - ext := filepath.Ext(path) - return ext == ".yaml" || ext == ".yml" -} - -// YAMLToJSON converts YAML unmarshaled data into json compatible data -func YAMLToJSON(data interface{}) (json.RawMessage, error) { - jm, err := transformData(data) - if err != nil { - return nil, err - } - b, err := WriteJSON(jm) - return json.RawMessage(b), err -} - -// BytesToYAMLDoc converts a byte slice into a YAML document -func BytesToYAMLDoc(data []byte) (interface{}, error) { - var document yaml.Node // preserve order that is present in the document - if err := yaml.Unmarshal(data, &document); err != nil { - return nil, err - } - if document.Kind != yaml.DocumentNode || len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode { - return nil, errors.New("only YAML documents that are objects are supported") - } - return &document, nil -} - -func yamlNode(root *yaml.Node) (interface{}, error) { - switch root.Kind { - case yaml.DocumentNode: - return yamlDocument(root) - case yaml.SequenceNode: - return yamlSequence(root) - case yaml.MappingNode: - return yamlMapping(root) - case yaml.ScalarNode: - return yamlScalar(root) - case yaml.AliasNode: - return yamlNode(root.Alias) - default: - return nil, fmt.Errorf("unsupported YAML node type: %v", root.Kind) - } -} - -func yamlDocument(node *yaml.Node) (interface{}, error) { - if len(node.Content) != 1 { - return nil, fmt.Errorf("unexpected YAML Document node content length: %d", len(node.Content)) - } - return yamlNode(node.Content[0]) -} - -func yamlMapping(node *yaml.Node) (interface{}, error) { - m := make(JSONMapSlice, len(node.Content)/2) - - var j int - for i := 0; i < len(node.Content); i += 2 { - var nmi JSONMapItem - k, err := yamlStringScalarC(node.Content[i]) - if err != nil { - return nil, fmt.Errorf("unable to decode YAML map key: %w", err) - } - nmi.Key = k - v, err := yamlNode(node.Content[i+1]) - if err != nil { - return nil, fmt.Errorf("unable to process YAML map value for key %q: %w", k, err) - } - nmi.Value = v - m[j] = nmi - j++ - } - return m, nil -} - -func yamlSequence(node *yaml.Node) (interface{}, error) { - s := make([]interface{}, 0) - - for i := 0; i < len(node.Content); i++ { - - v, err := yamlNode(node.Content[i]) - if err != nil { - return nil, fmt.Errorf("unable to decode YAML sequence value: %w", err) - } - s = append(s, v) - } - return s, nil -} - -const ( // See https://yaml.org/type/ - yamlStringScalar = "tag:yaml.org,2002:str" - yamlIntScalar = "tag:yaml.org,2002:int" - yamlBoolScalar = "tag:yaml.org,2002:bool" - yamlFloatScalar = "tag:yaml.org,2002:float" - yamlTimestamp = "tag:yaml.org,2002:timestamp" - yamlNull = "tag:yaml.org,2002:null" -) - -func yamlScalar(node *yaml.Node) (interface{}, error) { - switch node.LongTag() { - case yamlStringScalar: - return node.Value, nil - case yamlBoolScalar: - b, err := strconv.ParseBool(node.Value) - if err != nil { - return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting bool content: %w", node.Value, err) - } - return b, nil - case yamlIntScalar: - i, err := strconv.ParseInt(node.Value, 10, 64) - if err != nil { - return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting integer content: %w", node.Value, err) - } - return i, nil - case yamlFloatScalar: - f, err := strconv.ParseFloat(node.Value, 64) - if err != nil { - return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting float content: %w", node.Value, err) - } - return f, nil - case yamlTimestamp: - return node.Value, nil - case yamlNull: - return nil, nil //nolint:nilnil - default: - return nil, fmt.Errorf("YAML tag %q is not supported", node.LongTag()) - } -} - -func yamlStringScalarC(node *yaml.Node) (string, error) { - if node.Kind != yaml.ScalarNode { - return "", fmt.Errorf("expecting a string scalar but got %q", node.Kind) - } - switch node.LongTag() { - case yamlStringScalar, yamlIntScalar, yamlFloatScalar: - return node.Value, nil - default: - return "", fmt.Errorf("YAML tag %q is not supported as map key", node.LongTag()) - } -} - -// JSONMapSlice represent a JSON object, with the order of keys maintained -type JSONMapSlice []JSONMapItem - -// MarshalJSON renders a JSONMapSlice as JSON -func (s JSONMapSlice) MarshalJSON() ([]byte, error) { - w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty} - s.MarshalEasyJSON(w) - return w.BuildBytes() -} - -// MarshalEasyJSON renders a JSONMapSlice as JSON, using easyJSON -func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) { - w.RawByte('{') - - ln := len(s) - last := ln - 1 - for i := 0; i < ln; i++ { - s[i].MarshalEasyJSON(w) - if i != last { // last item - w.RawByte(',') - } - } - - w.RawByte('}') -} - -// UnmarshalJSON makes a JSONMapSlice from JSON -func (s *JSONMapSlice) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{Data: data} - s.UnmarshalEasyJSON(&l) - return l.Error() -} - -// UnmarshalEasyJSON makes a JSONMapSlice from JSON, using easyJSON -func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) { - if in.IsNull() { - in.Skip() - return - } - - var result JSONMapSlice - in.Delim('{') - for !in.IsDelim('}') { - var mi JSONMapItem - mi.UnmarshalEasyJSON(in) - result = append(result, mi) - } - *s = result -} - -func (s JSONMapSlice) MarshalYAML() (interface{}, error) { - var n yaml.Node - n.Kind = yaml.DocumentNode - var nodes []*yaml.Node - for _, item := range s { - nn, err := json2yaml(item.Value) - if err != nil { - return nil, err - } - ns := []*yaml.Node{ - { - Kind: yaml.ScalarNode, - Tag: yamlStringScalar, - Value: item.Key, - }, - nn, - } - nodes = append(nodes, ns...) - } - - n.Content = []*yaml.Node{ - { - Kind: yaml.MappingNode, - Content: nodes, - }, - } - - return yaml.Marshal(&n) -} - -func isNil(input interface{}) bool { - if input == nil { - return true - } - kind := reflect.TypeOf(input).Kind() - switch kind { //nolint:exhaustive - case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan: - return reflect.ValueOf(input).IsNil() - default: - return false - } -} - -func json2yaml(item interface{}) (*yaml.Node, error) { - if isNil(item) { - return &yaml.Node{ - Kind: yaml.ScalarNode, - Value: "null", - }, nil - } - - switch val := item.(type) { - case JSONMapSlice: - var n yaml.Node - n.Kind = yaml.MappingNode - for i := range val { - childNode, err := json2yaml(&val[i].Value) - if err != nil { - return nil, err - } - n.Content = append(n.Content, &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: yamlStringScalar, - Value: val[i].Key, - }, childNode) - } - return &n, nil - case map[string]interface{}: - var n yaml.Node - n.Kind = yaml.MappingNode - keys := make([]string, 0, len(val)) - for k := range val { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - v := val[k] - childNode, err := json2yaml(v) - if err != nil { - return nil, err - } - n.Content = append(n.Content, &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: yamlStringScalar, - Value: k, - }, childNode) - } - return &n, nil - case []interface{}: - var n yaml.Node - n.Kind = yaml.SequenceNode - for i := range val { - childNode, err := json2yaml(val[i]) - if err != nil { - return nil, err - } - n.Content = append(n.Content, childNode) - } - return &n, nil - case string: - return &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: yamlStringScalar, - Value: val, - }, nil - case float64: - return &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: yamlFloatScalar, - Value: strconv.FormatFloat(val, 'f', -1, 64), - }, nil - case int64: - return &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: yamlIntScalar, - Value: strconv.FormatInt(val, 10), - }, nil - case uint64: - return &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: yamlIntScalar, - Value: strconv.FormatUint(val, 10), - }, nil - case bool: - return &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: yamlBoolScalar, - Value: strconv.FormatBool(val), - }, nil - default: - return nil, fmt.Errorf("unhandled type: %T", val) - } -} - -// JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice -type JSONMapItem struct { - Key string - Value interface{} -} - -// MarshalJSON renders a JSONMapItem as JSON -func (s JSONMapItem) MarshalJSON() ([]byte, error) { - w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty} - s.MarshalEasyJSON(w) - return w.BuildBytes() -} - -// MarshalEasyJSON renders a JSONMapItem as JSON, using easyJSON -func (s JSONMapItem) MarshalEasyJSON(w *jwriter.Writer) { - w.String(s.Key) - w.RawByte(':') - w.Raw(WriteJSON(s.Value)) -} - -// UnmarshalJSON makes a JSONMapItem from JSON -func (s *JSONMapItem) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{Data: data} - s.UnmarshalEasyJSON(&l) - return l.Error() -} - -// UnmarshalEasyJSON makes a JSONMapItem from JSON, using easyJSON -func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) { - key := in.UnsafeString() - in.WantColon() - value := in.Interface() - in.WantComma() - s.Key = key - s.Value = value -} - -func transformData(input interface{}) (out interface{}, err error) { - format := func(t interface{}) (string, error) { - switch k := t.(type) { - case string: - return k, nil - case uint: - return strconv.FormatUint(uint64(k), 10), nil - case uint8: - return strconv.FormatUint(uint64(k), 10), nil - case uint16: - return strconv.FormatUint(uint64(k), 10), nil - case uint32: - return strconv.FormatUint(uint64(k), 10), nil - case uint64: - return strconv.FormatUint(k, 10), nil - case int: - return strconv.Itoa(k), nil - case int8: - return strconv.FormatInt(int64(k), 10), nil - case int16: - return strconv.FormatInt(int64(k), 10), nil - case int32: - return strconv.FormatInt(int64(k), 10), nil - case int64: - return strconv.FormatInt(k, 10), nil - default: - return "", fmt.Errorf("unexpected map key type, got: %T", k) - } - } - - switch in := input.(type) { - case yaml.Node: - return yamlNode(&in) - case *yaml.Node: - return yamlNode(in) - case map[interface{}]interface{}: - o := make(JSONMapSlice, 0, len(in)) - for ke, va := range in { - var nmi JSONMapItem - if nmi.Key, err = format(ke); err != nil { - return nil, err - } - - v, ert := transformData(va) - if ert != nil { - return nil, ert - } - nmi.Value = v - o = append(o, nmi) - } - return o, nil - case []interface{}: - len1 := len(in) - o := make([]interface{}, len1) - for i := 0; i < len1; i++ { - o[i], err = transformData(in[i]) - if err != nil { - return nil, err - } - } - return o, nil - } - return input, nil -} - -// YAMLDoc loads a yaml document from either http or a file and converts it to json -func YAMLDoc(path string) (json.RawMessage, error) { - yamlDoc, err := YAMLData(path) - if err != nil { - return nil, err - } - - data, err := YAMLToJSON(yamlDoc) - if err != nil { - return nil, err - } - - return data, nil -} - -// YAMLData loads a yaml document from either http or a file -func YAMLData(path string) (interface{}, error) { - data, err := LoadFromFileOrHTTP(path) - if err != nil { - return nil, err - } - - return BytesToYAMLDoc(data) -} diff --git a/vendor/github.com/golang-jwt/jwt/.gitignore b/vendor/github.com/golang-jwt/jwt/.gitignore deleted file mode 100644 index 09573e016..000000000 --- a/vendor/github.com/golang-jwt/jwt/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.DS_Store -bin -.idea/ - diff --git a/vendor/github.com/golang-jwt/jwt/LICENSE b/vendor/github.com/golang-jwt/jwt/LICENSE deleted file mode 100644 index 35dbc2520..000000000 --- a/vendor/github.com/golang-jwt/jwt/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) 2012 Dave Grijalva -Copyright (c) 2021 golang-jwt maintainers - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/vendor/github.com/golang-jwt/jwt/MIGRATION_GUIDE.md b/vendor/github.com/golang-jwt/jwt/MIGRATION_GUIDE.md deleted file mode 100644 index c4efbd2a8..000000000 --- a/vendor/github.com/golang-jwt/jwt/MIGRATION_GUIDE.md +++ /dev/null @@ -1,22 +0,0 @@ -## Migration Guide (v3.2.1) - -Starting from [v3.2.1](https://github.com/golang-jwt/jwt/releases/tag/v3.2.1]), the import path has changed from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt`. Future releases will be using the `github.com/golang-jwt/jwt` import path and continue the existing versioning scheme of `v3.x.x+incompatible`. Backwards-compatible patches and fixes will be done on the `v3` release branch, where as new build-breaking features will be developed in a `v4` release, possibly including a SIV-style import path. - -### go.mod replacement - -In a first step, the easiest way is to use `go mod edit` to issue a replacement. - -``` -go mod edit -replace github.com/dgrijalva/jwt-go=github.com/golang-jwt/jwt@v3.2.1+incompatible -go mod tidy -``` - -This will still keep the old import path in your code but replace it with the new package and also introduce a new indirect dependency to `github.com/golang-jwt/jwt`. Try to compile your project; it should still work. - -### Cleanup - -If your code still consistently builds, you can replace all occurences of `github.com/dgrijalva/jwt-go` with `github.com/golang-jwt/jwt`, either manually or by using tools such as `sed`. Finally, the `replace` directive in the `go.mod` file can be removed. - -## Older releases (before v3.2.0) - -The original migration guide for older releases can be found at https://github.com/dgrijalva/jwt-go/blob/master/MIGRATION_GUIDE.md. \ No newline at end of file diff --git a/vendor/github.com/golang-jwt/jwt/README.md b/vendor/github.com/golang-jwt/jwt/README.md deleted file mode 100644 index 9b653e46b..000000000 --- a/vendor/github.com/golang-jwt/jwt/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# jwt-go - -[![build](https://github.com/golang-jwt/jwt/actions/workflows/build.yml/badge.svg)](https://github.com/golang-jwt/jwt/actions/workflows/build.yml) -[![Go Reference](https://pkg.go.dev/badge/github.com/golang-jwt/jwt.svg)](https://pkg.go.dev/github.com/golang-jwt/jwt) - -A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](https://datatracker.ietf.org/doc/html/rfc7519). - -**IMPORT PATH CHANGE:** Starting from [v3.2.1](https://github.com/golang-jwt/jwt/releases/tag/v3.2.1), the import path has changed from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt`. After the original author of the library suggested migrating the maintenance of `jwt-go`, a dedicated team of open source maintainers decided to clone the existing library into this repository. See [dgrijalva/jwt-go#462](https://github.com/dgrijalva/jwt-go/issues/462) for a detailed discussion on this topic. - -Future releases will be using the `github.com/golang-jwt/jwt` import path and continue the existing versioning scheme of `v3.x.x+incompatible`. Backwards-compatible patches and fixes will be done on the `v3` release branch, where as new build-breaking features will be developed in a `v4` release, possibly including a SIV-style import path. - -**SECURITY NOTICE:** Some older versions of Go have a security issue in the crypto/elliptic. Recommendation is to upgrade to at least 1.15 See issue [dgrijalva/jwt-go#216](https://github.com/dgrijalva/jwt-go/issues/216) for more detail. - -**SECURITY NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided. - -### Supported Go versions - -Our support of Go versions is aligned with Go's [version release policy](https://golang.org/doc/devel/release#policy). -So we will support a major version of Go until there are two newer major releases. -We no longer support building jwt-go with unsupported Go versions, as these contain security vulnerabilities -which will not be fixed. - -## What the heck is a JWT? - -JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens. - -In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](https://datatracker.ietf.org/doc/html/rfc4648) encoded. The last part is the signature, encoded the same way. - -The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used. - -The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) for information about reserved keys and the proper way to add your own. - -## What's in the box? - -This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. - -## Examples - -See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt) for examples of usage: - -* [Simple example of parsing and validating a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-Parse-Hmac) -* [Simple example of building and signing a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-New-Hmac) -* [Directory of Examples](https://pkg.go.dev/github.com/golang-jwt/jwt#pkg-examples) - -## Extensions - -This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. - -Here's an example of an extension that integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS): https://github.com/someone1/gcp-jwt-go - -## Compliance - -This library was last reviewed to comply with [RTF 7519](https://datatracker.ietf.org/doc/html/rfc7519) dated May 2015 with a few notable differences: - -* In order to protect against accidental use of [Unsecured JWTs](https://datatracker.ietf.org/doc/html/rfc7519#section-6), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key. - -## Project Status & Versioning - -This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason). - -This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `main`. Periodically, versions will be tagged from `main`. You can find all the releases on [the project releases page](https://github.com/golang-jwt/jwt/releases). - -While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/golang-jwt/jwt.v3`. It will do the right thing WRT semantic versioning. - -**BREAKING CHANGES:*** -* Version 3.0.0 includes _a lot_ of changes from the 2.x line, including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. - -## Usage Tips - -### Signing vs Encryption - -A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data: - -* The author of the token was in the possession of the signing secret -* The data has not been modified since it was signed - -It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library. - -### Choosing a Signing Method - -There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric. - -Symmetric signing methods, such as HSA, use only a single secret. This is probably the simplest signing method to use since any `[]byte` can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation. - -Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification. - -### Signing Methods and Key Types - -Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones: - -* The [HMAC signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation -* The [RSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation -* The [ECDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation - -### JWT and OAuth - -It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication. - -Without going too far down the rabbit hole, here's a description of the interaction of these technologies: - -* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth. -* OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token. -* Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL. - -### Troubleshooting - -This library uses descriptive error messages whenever possible. If you are not getting the expected result, have a look at the errors. The most common place people get stuck is providing the correct type of key to the parser. See the above section on signing methods and key types. - -## More - -Documentation can be found [on pkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt). - -The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation. diff --git a/vendor/github.com/golang-jwt/jwt/VERSION_HISTORY.md b/vendor/github.com/golang-jwt/jwt/VERSION_HISTORY.md deleted file mode 100644 index 637f2ba61..000000000 --- a/vendor/github.com/golang-jwt/jwt/VERSION_HISTORY.md +++ /dev/null @@ -1,131 +0,0 @@ -## `jwt-go` Version History - -#### 3.2.2 - -* Starting from this release, we are adopting the policy to support the most 2 recent versions of Go currently available. By the time of this release, this is Go 1.15 and 1.16 ([#28](https://github.com/golang-jwt/jwt/pull/28)). -* Fixed a potential issue that could occur when the verification of `exp`, `iat` or `nbf` was not required and contained invalid contents, i.e. non-numeric/date. Thanks for @thaJeztah for making us aware of that and @giorgos-f3 for originally reporting it to the formtech fork ([#40](https://github.com/golang-jwt/jwt/pull/40)). -* Added support for EdDSA / ED25519 ([#36](https://github.com/golang-jwt/jwt/pull/36)). -* Optimized allocations ([#33](https://github.com/golang-jwt/jwt/pull/33)). - -#### 3.2.1 - -* **Import Path Change**: See MIGRATION_GUIDE.md for tips on updating your code - * Changed the import path from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt` -* Fixed type confusing issue between `string` and `[]string` in `VerifyAudience` ([#12](https://github.com/golang-jwt/jwt/pull/12)). This fixes CVE-2020-26160 - -#### 3.2.0 - -* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation -* HMAC signing method returns `ErrInvalidKeyType` instead of `ErrInvalidKey` where appropriate -* Added options to `request.ParseFromRequest`, which allows for an arbitrary list of modifiers to parsing behavior. Initial set include `WithClaims` and `WithParser`. Existing usage of this function will continue to work as before. -* Deprecated `ParseFromRequestWithClaims` to simplify API in the future. - -#### 3.1.0 - -* Improvements to `jwt` command line tool -* Added `SkipClaimsValidation` option to `Parser` -* Documentation updates - -#### 3.0.0 - -* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code - * Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods. - * `ParseFromRequest` has been moved to `request` subpackage and usage has changed - * The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims. -* Other Additions and Changes - * Added `Claims` interface type to allow users to decode the claims into a custom type - * Added `ParseWithClaims`, which takes a third argument of type `Claims`. Use this function instead of `Parse` if you have a custom type you'd like to decode into. - * Dramatically improved the functionality and flexibility of `ParseFromRequest`, which is now in the `request` subpackage - * Added `ParseFromRequestWithClaims` which is the `FromRequest` equivalent of `ParseWithClaims` - * Added new interface type `Extractor`, which is used for extracting JWT strings from http requests. Used with `ParseFromRequest` and `ParseFromRequestWithClaims`. - * Added several new, more specific, validation errors to error type bitmask - * Moved examples from README to executable example files - * Signing method registry is now thread safe - * Added new property to `ValidationError`, which contains the raw error returned by calls made by parse/verify (such as those returned by keyfunc or json parser) - -#### 2.7.0 - -This will likely be the last backwards compatible release before 3.0.0, excluding essential bug fixes. - -* Added new option `-show` to the `jwt` command that will just output the decoded token without verifying -* Error text for expired tokens includes how long it's been expired -* Fixed incorrect error returned from `ParseRSAPublicKeyFromPEM` -* Documentation updates - -#### 2.6.0 - -* Exposed inner error within ValidationError -* Fixed validation errors when using UseJSONNumber flag -* Added several unit tests - -#### 2.5.0 - -* Added support for signing method none. You shouldn't use this. The API tries to make this clear. -* Updated/fixed some documentation -* Added more helpful error message when trying to parse tokens that begin with `BEARER ` - -#### 2.4.0 - -* Added new type, Parser, to allow for configuration of various parsing parameters - * You can now specify a list of valid signing methods. Anything outside this set will be rejected. - * You can now opt to use the `json.Number` type instead of `float64` when parsing token JSON -* Added support for [Travis CI](https://travis-ci.org/dgrijalva/jwt-go) -* Fixed some bugs with ECDSA parsing - -#### 2.3.0 - -* Added support for ECDSA signing methods -* Added support for RSA PSS signing methods (requires go v1.4) - -#### 2.2.0 - -* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`. Result will now be the parsed token and an error, instead of a panic. - -#### 2.1.0 - -Backwards compatible API change that was missed in 2.0.0. - -* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte` - -#### 2.0.0 - -There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change. - -The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. - -It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`. - -* **Compatibility Breaking Changes** - * `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct` - * `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct` - * `KeyFunc` now returns `interface{}` instead of `[]byte` - * `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key - * `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key -* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`. Specific sizes are now just instances of this type. - * Added public package global `SigningMethodHS256` - * Added public package global `SigningMethodHS384` - * Added public package global `SigningMethodHS512` -* Renamed type `SigningMethodRS256` to `SigningMethodRSA`. Specific sizes are now just instances of this type. - * Added public package global `SigningMethodRS256` - * Added public package global `SigningMethodRS384` - * Added public package global `SigningMethodRS512` -* Moved sample private key for HMAC tests from an inline value to a file on disk. Value is unchanged. -* Refactored the RSA implementation to be easier to read -* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM` - -#### 1.0.2 - -* Fixed bug in parsing public keys from certificates -* Added more tests around the parsing of keys for RS256 -* Code refactoring in RS256 implementation. No functional changes - -#### 1.0.1 - -* Fixed panic if RS256 signing method was passed an invalid key - -#### 1.0.0 - -* First versioned release -* API stabilized -* Supports creating, signing, parsing, and validating JWT tokens -* Supports RS256 and HS256 signing methods diff --git a/vendor/github.com/golang-jwt/jwt/claims.go b/vendor/github.com/golang-jwt/jwt/claims.go deleted file mode 100644 index f1dba3cb9..000000000 --- a/vendor/github.com/golang-jwt/jwt/claims.go +++ /dev/null @@ -1,146 +0,0 @@ -package jwt - -import ( - "crypto/subtle" - "fmt" - "time" -) - -// For a type to be a Claims object, it must just have a Valid method that determines -// if the token is invalid for any supported reason -type Claims interface { - Valid() error -} - -// Structured version of Claims Section, as referenced at -// https://tools.ietf.org/html/rfc7519#section-4.1 -// See examples for how to use this with your own claim types -type StandardClaims struct { - Audience string `json:"aud,omitempty"` - ExpiresAt int64 `json:"exp,omitempty"` - Id string `json:"jti,omitempty"` - IssuedAt int64 `json:"iat,omitempty"` - Issuer string `json:"iss,omitempty"` - NotBefore int64 `json:"nbf,omitempty"` - Subject string `json:"sub,omitempty"` -} - -// Validates time based claims "exp, iat, nbf". -// There is no accounting for clock skew. -// As well, if any of the above claims are not in the token, it will still -// be considered a valid claim. -func (c StandardClaims) Valid() error { - vErr := new(ValidationError) - now := TimeFunc().Unix() - - // The claims below are optional, by default, so if they are set to the - // default value in Go, let's not fail the verification for them. - if !c.VerifyExpiresAt(now, false) { - delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) - vErr.Inner = fmt.Errorf("token is expired by %v", delta) - vErr.Errors |= ValidationErrorExpired - } - - if !c.VerifyIssuedAt(now, false) { - vErr.Inner = fmt.Errorf("Token used before issued") - vErr.Errors |= ValidationErrorIssuedAt - } - - if !c.VerifyNotBefore(now, false) { - vErr.Inner = fmt.Errorf("token is not valid yet") - vErr.Errors |= ValidationErrorNotValidYet - } - - if vErr.valid() { - return nil - } - - return vErr -} - -// Compares the aud claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool { - return verifyAud([]string{c.Audience}, cmp, req) -} - -// Compares the exp claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool { - return verifyExp(c.ExpiresAt, cmp, req) -} - -// Compares the iat claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool { - return verifyIat(c.IssuedAt, cmp, req) -} - -// Compares the iss claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool { - return verifyIss(c.Issuer, cmp, req) -} - -// Compares the nbf claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { - return verifyNbf(c.NotBefore, cmp, req) -} - -// ----- helpers - -func verifyAud(aud []string, cmp string, required bool) bool { - if len(aud) == 0 { - return !required - } - // use a var here to keep constant time compare when looping over a number of claims - result := false - - var stringClaims string - for _, a := range aud { - if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 { - result = true - } - stringClaims = stringClaims + a - } - - // case where "" is sent in one or many aud claims - if len(stringClaims) == 0 { - return !required - } - - return result -} - -func verifyExp(exp int64, now int64, required bool) bool { - if exp == 0 { - return !required - } - return now <= exp -} - -func verifyIat(iat int64, now int64, required bool) bool { - if iat == 0 { - return !required - } - return now >= iat -} - -func verifyIss(iss string, cmp string, required bool) bool { - if iss == "" { - return !required - } - if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 { - return true - } else { - return false - } -} - -func verifyNbf(nbf int64, now int64, required bool) bool { - if nbf == 0 { - return !required - } - return now >= nbf -} diff --git a/vendor/github.com/golang-jwt/jwt/doc.go b/vendor/github.com/golang-jwt/jwt/doc.go deleted file mode 100644 index a86dc1a3b..000000000 --- a/vendor/github.com/golang-jwt/jwt/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html -// -// See README.md for more info. -package jwt diff --git a/vendor/github.com/golang-jwt/jwt/ecdsa.go b/vendor/github.com/golang-jwt/jwt/ecdsa.go deleted file mode 100644 index 15e23435d..000000000 --- a/vendor/github.com/golang-jwt/jwt/ecdsa.go +++ /dev/null @@ -1,142 +0,0 @@ -package jwt - -import ( - "crypto" - "crypto/ecdsa" - "crypto/rand" - "errors" - "math/big" -) - -var ( - // Sadly this is missing from crypto/ecdsa compared to crypto/rsa - ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") -) - -// Implements the ECDSA family of signing methods signing methods -// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification -type SigningMethodECDSA struct { - Name string - Hash crypto.Hash - KeySize int - CurveBits int -} - -// Specific instances for EC256 and company -var ( - SigningMethodES256 *SigningMethodECDSA - SigningMethodES384 *SigningMethodECDSA - SigningMethodES512 *SigningMethodECDSA -) - -func init() { - // ES256 - SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256} - RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { - return SigningMethodES256 - }) - - // ES384 - SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384} - RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { - return SigningMethodES384 - }) - - // ES512 - SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521} - RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { - return SigningMethodES512 - }) -} - -func (m *SigningMethodECDSA) Alg() string { - return m.Name -} - -// Implements the Verify method from SigningMethod -// For this verify method, key must be an ecdsa.PublicKey struct -func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { - var err error - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - - // Get the key - var ecdsaKey *ecdsa.PublicKey - switch k := key.(type) { - case *ecdsa.PublicKey: - ecdsaKey = k - default: - return ErrInvalidKeyType - } - - if len(sig) != 2*m.KeySize { - return ErrECDSAVerification - } - - r := big.NewInt(0).SetBytes(sig[:m.KeySize]) - s := big.NewInt(0).SetBytes(sig[m.KeySize:]) - - // Create hasher - if !m.Hash.Available() { - return ErrHashUnavailable - } - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Verify the signature - if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus { - return nil - } - - return ErrECDSAVerification -} - -// Implements the Sign method from SigningMethod -// For this signing method, key must be an ecdsa.PrivateKey struct -func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { - // Get the key - var ecdsaKey *ecdsa.PrivateKey - switch k := key.(type) { - case *ecdsa.PrivateKey: - ecdsaKey = k - default: - return "", ErrInvalidKeyType - } - - // Create the hasher - if !m.Hash.Available() { - return "", ErrHashUnavailable - } - - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Sign the string and return r, s - if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { - curveBits := ecdsaKey.Curve.Params().BitSize - - if m.CurveBits != curveBits { - return "", ErrInvalidKey - } - - keyBytes := curveBits / 8 - if curveBits%8 > 0 { - keyBytes += 1 - } - - // We serialize the outputs (r and s) into big-endian byte arrays - // padded with zeros on the left to make sure the sizes work out. - // Output must be 2*keyBytes long. - out := make([]byte, 2*keyBytes) - r.FillBytes(out[0:keyBytes]) // r is assigned to the first half of output. - s.FillBytes(out[keyBytes:]) // s is assigned to the second half of output. - - return EncodeSegment(out), nil - } else { - return "", err - } -} diff --git a/vendor/github.com/golang-jwt/jwt/ecdsa_utils.go b/vendor/github.com/golang-jwt/jwt/ecdsa_utils.go deleted file mode 100644 index db9f4be7d..000000000 --- a/vendor/github.com/golang-jwt/jwt/ecdsa_utils.go +++ /dev/null @@ -1,69 +0,0 @@ -package jwt - -import ( - "crypto/ecdsa" - "crypto/x509" - "encoding/pem" - "errors" -) - -var ( - ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key") - ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key") -) - -// Parse PEM encoded Elliptic Curve Private Key Structure -func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - // Parse the key - var parsedKey interface{} - if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { - if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { - return nil, err - } - } - - var pkey *ecdsa.PrivateKey - var ok bool - if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok { - return nil, ErrNotECPrivateKey - } - - return pkey, nil -} - -// Parse PEM encoded PKCS1 or PKCS8 public key -func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - // Parse the key - var parsedKey interface{} - if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { - if cert, err := x509.ParseCertificate(block.Bytes); err == nil { - parsedKey = cert.PublicKey - } else { - return nil, err - } - } - - var pkey *ecdsa.PublicKey - var ok bool - if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok { - return nil, ErrNotECPublicKey - } - - return pkey, nil -} diff --git a/vendor/github.com/golang-jwt/jwt/ed25519.go b/vendor/github.com/golang-jwt/jwt/ed25519.go deleted file mode 100644 index a2f8ddbe9..000000000 --- a/vendor/github.com/golang-jwt/jwt/ed25519.go +++ /dev/null @@ -1,81 +0,0 @@ -package jwt - -import ( - "errors" - - "crypto/ed25519" -) - -var ( - ErrEd25519Verification = errors.New("ed25519: verification error") -) - -// Implements the EdDSA family -// Expects ed25519.PrivateKey for signing and ed25519.PublicKey for verification -type SigningMethodEd25519 struct{} - -// Specific instance for EdDSA -var ( - SigningMethodEdDSA *SigningMethodEd25519 -) - -func init() { - SigningMethodEdDSA = &SigningMethodEd25519{} - RegisterSigningMethod(SigningMethodEdDSA.Alg(), func() SigningMethod { - return SigningMethodEdDSA - }) -} - -func (m *SigningMethodEd25519) Alg() string { - return "EdDSA" -} - -// Implements the Verify method from SigningMethod -// For this verify method, key must be an ed25519.PublicKey -func (m *SigningMethodEd25519) Verify(signingString, signature string, key interface{}) error { - var err error - var ed25519Key ed25519.PublicKey - var ok bool - - if ed25519Key, ok = key.(ed25519.PublicKey); !ok { - return ErrInvalidKeyType - } - - if len(ed25519Key) != ed25519.PublicKeySize { - return ErrInvalidKey - } - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - - // Verify the signature - if !ed25519.Verify(ed25519Key, []byte(signingString), sig) { - return ErrEd25519Verification - } - - return nil -} - -// Implements the Sign method from SigningMethod -// For this signing method, key must be an ed25519.PrivateKey -func (m *SigningMethodEd25519) Sign(signingString string, key interface{}) (string, error) { - var ed25519Key ed25519.PrivateKey - var ok bool - - if ed25519Key, ok = key.(ed25519.PrivateKey); !ok { - return "", ErrInvalidKeyType - } - - // ed25519.Sign panics if private key not equal to ed25519.PrivateKeySize - // this allows to avoid recover usage - if len(ed25519Key) != ed25519.PrivateKeySize { - return "", ErrInvalidKey - } - - // Sign the string and return the encoded result - sig := ed25519.Sign(ed25519Key, []byte(signingString)) - return EncodeSegment(sig), nil -} diff --git a/vendor/github.com/golang-jwt/jwt/ed25519_utils.go b/vendor/github.com/golang-jwt/jwt/ed25519_utils.go deleted file mode 100644 index c6357275e..000000000 --- a/vendor/github.com/golang-jwt/jwt/ed25519_utils.go +++ /dev/null @@ -1,64 +0,0 @@ -package jwt - -import ( - "crypto" - "crypto/ed25519" - "crypto/x509" - "encoding/pem" - "errors" -) - -var ( - ErrNotEdPrivateKey = errors.New("Key is not a valid Ed25519 private key") - ErrNotEdPublicKey = errors.New("Key is not a valid Ed25519 public key") -) - -// Parse PEM-encoded Edwards curve private key -func ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - // Parse the key - var parsedKey interface{} - if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { - return nil, err - } - - var pkey ed25519.PrivateKey - var ok bool - if pkey, ok = parsedKey.(ed25519.PrivateKey); !ok { - return nil, ErrNotEdPrivateKey - } - - return pkey, nil -} - -// Parse PEM-encoded Edwards curve public key -func ParseEdPublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - // Parse the key - var parsedKey interface{} - if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { - return nil, err - } - - var pkey ed25519.PublicKey - var ok bool - if pkey, ok = parsedKey.(ed25519.PublicKey); !ok { - return nil, ErrNotEdPublicKey - } - - return pkey, nil -} diff --git a/vendor/github.com/golang-jwt/jwt/errors.go b/vendor/github.com/golang-jwt/jwt/errors.go deleted file mode 100644 index 1c93024aa..000000000 --- a/vendor/github.com/golang-jwt/jwt/errors.go +++ /dev/null @@ -1,59 +0,0 @@ -package jwt - -import ( - "errors" -) - -// Error constants -var ( - ErrInvalidKey = errors.New("key is invalid") - ErrInvalidKeyType = errors.New("key is of invalid type") - ErrHashUnavailable = errors.New("the requested hash function is unavailable") -) - -// The errors that might occur when parsing and validating a token -const ( - ValidationErrorMalformed uint32 = 1 << iota // Token is malformed - ValidationErrorUnverifiable // Token could not be verified because of signing problems - ValidationErrorSignatureInvalid // Signature validation failed - - // Standard Claim validation errors - ValidationErrorAudience // AUD validation failed - ValidationErrorExpired // EXP validation failed - ValidationErrorIssuedAt // IAT validation failed - ValidationErrorIssuer // ISS validation failed - ValidationErrorNotValidYet // NBF validation failed - ValidationErrorId // JTI validation failed - ValidationErrorClaimsInvalid // Generic claims validation error -) - -// Helper for constructing a ValidationError with a string error message -func NewValidationError(errorText string, errorFlags uint32) *ValidationError { - return &ValidationError{ - text: errorText, - Errors: errorFlags, - } -} - -// The error from Parse if token is not valid -type ValidationError struct { - Inner error // stores the error returned by external dependencies, i.e.: KeyFunc - Errors uint32 // bitfield. see ValidationError... constants - text string // errors that do not have a valid error just have text -} - -// Validation error is an error type -func (e ValidationError) Error() string { - if e.Inner != nil { - return e.Inner.Error() - } else if e.text != "" { - return e.text - } else { - return "token is invalid" - } -} - -// No errors -func (e *ValidationError) valid() bool { - return e.Errors == 0 -} diff --git a/vendor/github.com/golang-jwt/jwt/hmac.go b/vendor/github.com/golang-jwt/jwt/hmac.go deleted file mode 100644 index addbe5d40..000000000 --- a/vendor/github.com/golang-jwt/jwt/hmac.go +++ /dev/null @@ -1,95 +0,0 @@ -package jwt - -import ( - "crypto" - "crypto/hmac" - "errors" -) - -// Implements the HMAC-SHA family of signing methods signing methods -// Expects key type of []byte for both signing and validation -type SigningMethodHMAC struct { - Name string - Hash crypto.Hash -} - -// Specific instances for HS256 and company -var ( - SigningMethodHS256 *SigningMethodHMAC - SigningMethodHS384 *SigningMethodHMAC - SigningMethodHS512 *SigningMethodHMAC - ErrSignatureInvalid = errors.New("signature is invalid") -) - -func init() { - // HS256 - SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256} - RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod { - return SigningMethodHS256 - }) - - // HS384 - SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384} - RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod { - return SigningMethodHS384 - }) - - // HS512 - SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512} - RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod { - return SigningMethodHS512 - }) -} - -func (m *SigningMethodHMAC) Alg() string { - return m.Name -} - -// Verify the signature of HSXXX tokens. Returns nil if the signature is valid. -func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error { - // Verify the key is the right type - keyBytes, ok := key.([]byte) - if !ok { - return ErrInvalidKeyType - } - - // Decode signature, for comparison - sig, err := DecodeSegment(signature) - if err != nil { - return err - } - - // Can we use the specified hashing method? - if !m.Hash.Available() { - return ErrHashUnavailable - } - - // This signing method is symmetric, so we validate the signature - // by reproducing the signature from the signing string and key, then - // comparing that against the provided signature. - hasher := hmac.New(m.Hash.New, keyBytes) - hasher.Write([]byte(signingString)) - if !hmac.Equal(sig, hasher.Sum(nil)) { - return ErrSignatureInvalid - } - - // No validation errors. Signature is good. - return nil -} - -// Implements the Sign method from SigningMethod for this signing method. -// Key must be []byte -func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) { - if keyBytes, ok := key.([]byte); ok { - if !m.Hash.Available() { - return "", ErrHashUnavailable - } - - hasher := hmac.New(m.Hash.New, keyBytes) - hasher.Write([]byte(signingString)) - - return EncodeSegment(hasher.Sum(nil)), nil - } - - return "", ErrInvalidKeyType -} diff --git a/vendor/github.com/golang-jwt/jwt/map_claims.go b/vendor/github.com/golang-jwt/jwt/map_claims.go deleted file mode 100644 index 72c79f92e..000000000 --- a/vendor/github.com/golang-jwt/jwt/map_claims.go +++ /dev/null @@ -1,120 +0,0 @@ -package jwt - -import ( - "encoding/json" - "errors" - // "fmt" -) - -// Claims type that uses the map[string]interface{} for JSON decoding -// This is the default claims type if you don't supply one -type MapClaims map[string]interface{} - -// VerifyAudience Compares the aud claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyAudience(cmp string, req bool) bool { - var aud []string - switch v := m["aud"].(type) { - case string: - aud = append(aud, v) - case []string: - aud = v - case []interface{}: - for _, a := range v { - vs, ok := a.(string) - if !ok { - return false - } - aud = append(aud, vs) - } - } - return verifyAud(aud, cmp, req) -} - -// Compares the exp claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool { - exp, ok := m["exp"] - if !ok { - return !req - } - switch expType := exp.(type) { - case float64: - return verifyExp(int64(expType), cmp, req) - case json.Number: - v, _ := expType.Int64() - return verifyExp(v, cmp, req) - } - return false -} - -// Compares the iat claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool { - iat, ok := m["iat"] - if !ok { - return !req - } - switch iatType := iat.(type) { - case float64: - return verifyIat(int64(iatType), cmp, req) - case json.Number: - v, _ := iatType.Int64() - return verifyIat(v, cmp, req) - } - return false -} - -// Compares the iss claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyIssuer(cmp string, req bool) bool { - iss, _ := m["iss"].(string) - return verifyIss(iss, cmp, req) -} - -// Compares the nbf claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool { - nbf, ok := m["nbf"] - if !ok { - return !req - } - switch nbfType := nbf.(type) { - case float64: - return verifyNbf(int64(nbfType), cmp, req) - case json.Number: - v, _ := nbfType.Int64() - return verifyNbf(v, cmp, req) - } - return false -} - -// Validates time based claims "exp, iat, nbf". -// There is no accounting for clock skew. -// As well, if any of the above claims are not in the token, it will still -// be considered a valid claim. -func (m MapClaims) Valid() error { - vErr := new(ValidationError) - now := TimeFunc().Unix() - - if !m.VerifyExpiresAt(now, false) { - vErr.Inner = errors.New("Token is expired") - vErr.Errors |= ValidationErrorExpired - } - - if !m.VerifyIssuedAt(now, false) { - vErr.Inner = errors.New("Token used before issued") - vErr.Errors |= ValidationErrorIssuedAt - } - - if !m.VerifyNotBefore(now, false) { - vErr.Inner = errors.New("Token is not valid yet") - vErr.Errors |= ValidationErrorNotValidYet - } - - if vErr.valid() { - return nil - } - - return vErr -} diff --git a/vendor/github.com/golang-jwt/jwt/none.go b/vendor/github.com/golang-jwt/jwt/none.go deleted file mode 100644 index f04d189d0..000000000 --- a/vendor/github.com/golang-jwt/jwt/none.go +++ /dev/null @@ -1,52 +0,0 @@ -package jwt - -// Implements the none signing method. This is required by the spec -// but you probably should never use it. -var SigningMethodNone *signingMethodNone - -const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed" - -var NoneSignatureTypeDisallowedError error - -type signingMethodNone struct{} -type unsafeNoneMagicConstant string - -func init() { - SigningMethodNone = &signingMethodNone{} - NoneSignatureTypeDisallowedError = NewValidationError("'none' signature type is not allowed", ValidationErrorSignatureInvalid) - - RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod { - return SigningMethodNone - }) -} - -func (m *signingMethodNone) Alg() string { - return "none" -} - -// Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key -func (m *signingMethodNone) Verify(signingString, signature string, key interface{}) (err error) { - // Key must be UnsafeAllowNoneSignatureType to prevent accidentally - // accepting 'none' signing method - if _, ok := key.(unsafeNoneMagicConstant); !ok { - return NoneSignatureTypeDisallowedError - } - // If signing method is none, signature must be an empty string - if signature != "" { - return NewValidationError( - "'none' signing method with non-empty signature", - ValidationErrorSignatureInvalid, - ) - } - - // Accept 'none' signing method. - return nil -} - -// Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key -func (m *signingMethodNone) Sign(signingString string, key interface{}) (string, error) { - if _, ok := key.(unsafeNoneMagicConstant); ok { - return "", nil - } - return "", NoneSignatureTypeDisallowedError -} diff --git a/vendor/github.com/golang-jwt/jwt/parser.go b/vendor/github.com/golang-jwt/jwt/parser.go deleted file mode 100644 index d6901d9ad..000000000 --- a/vendor/github.com/golang-jwt/jwt/parser.go +++ /dev/null @@ -1,148 +0,0 @@ -package jwt - -import ( - "bytes" - "encoding/json" - "fmt" - "strings" -) - -type Parser struct { - ValidMethods []string // If populated, only these methods will be considered valid - UseJSONNumber bool // Use JSON Number format in JSON decoder - SkipClaimsValidation bool // Skip claims validation during token parsing -} - -// Parse, validate, and return a token. -// keyFunc will receive the parsed token and should return the key for validating. -// If everything is kosher, err will be nil -func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { - return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) -} - -func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { - token, parts, err := p.ParseUnverified(tokenString, claims) - if err != nil { - return token, err - } - - // Verify signing method is in the required set - if p.ValidMethods != nil { - var signingMethodValid = false - var alg = token.Method.Alg() - for _, m := range p.ValidMethods { - if m == alg { - signingMethodValid = true - break - } - } - if !signingMethodValid { - // signing method is not in the listed set - return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid) - } - } - - // Lookup key - var key interface{} - if keyFunc == nil { - // keyFunc was not provided. short circuiting validation - return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable) - } - if key, err = keyFunc(token); err != nil { - // keyFunc returned an error - if ve, ok := err.(*ValidationError); ok { - return token, ve - } - return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable} - } - - vErr := &ValidationError{} - - // Validate Claims - if !p.SkipClaimsValidation { - if err := token.Claims.Valid(); err != nil { - - // If the Claims Valid returned an error, check if it is a validation error, - // If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set - if e, ok := err.(*ValidationError); !ok { - vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid} - } else { - vErr = e - } - } - } - - // Perform validation - token.Signature = parts[2] - if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { - vErr.Inner = err - vErr.Errors |= ValidationErrorSignatureInvalid - } - - if vErr.valid() { - token.Valid = true - return token, nil - } - - return token, vErr -} - -// WARNING: Don't use this method unless you know what you're doing -// -// This method parses the token but doesn't validate the signature. It's only -// ever useful in cases where you know the signature is valid (because it has -// been checked previously in the stack) and you want to extract values from -// it. -func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) { - parts = strings.Split(tokenString, ".") - if len(parts) != 3 { - return nil, parts, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) - } - - token = &Token{Raw: tokenString} - - // parse Header - var headerBytes []byte - if headerBytes, err = DecodeSegment(parts[0]); err != nil { - if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") { - return token, parts, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed) - } - return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} - } - if err = json.Unmarshal(headerBytes, &token.Header); err != nil { - return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} - } - - // parse Claims - var claimBytes []byte - token.Claims = claims - - if claimBytes, err = DecodeSegment(parts[1]); err != nil { - return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} - } - dec := json.NewDecoder(bytes.NewBuffer(claimBytes)) - if p.UseJSONNumber { - dec.UseNumber() - } - // JSON Decode. Special case for map type to avoid weird pointer behavior - if c, ok := token.Claims.(MapClaims); ok { - err = dec.Decode(&c) - } else { - err = dec.Decode(&claims) - } - // Handle decode error - if err != nil { - return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} - } - - // Lookup signature method - if method, ok := token.Header["alg"].(string); ok { - if token.Method = GetSigningMethod(method); token.Method == nil { - return token, parts, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable) - } - } else { - return token, parts, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable) - } - - return token, parts, nil -} diff --git a/vendor/github.com/golang-jwt/jwt/rsa.go b/vendor/github.com/golang-jwt/jwt/rsa.go deleted file mode 100644 index e4caf1ca4..000000000 --- a/vendor/github.com/golang-jwt/jwt/rsa.go +++ /dev/null @@ -1,101 +0,0 @@ -package jwt - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" -) - -// Implements the RSA family of signing methods signing methods -// Expects *rsa.PrivateKey for signing and *rsa.PublicKey for validation -type SigningMethodRSA struct { - Name string - Hash crypto.Hash -} - -// Specific instances for RS256 and company -var ( - SigningMethodRS256 *SigningMethodRSA - SigningMethodRS384 *SigningMethodRSA - SigningMethodRS512 *SigningMethodRSA -) - -func init() { - // RS256 - SigningMethodRS256 = &SigningMethodRSA{"RS256", crypto.SHA256} - RegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod { - return SigningMethodRS256 - }) - - // RS384 - SigningMethodRS384 = &SigningMethodRSA{"RS384", crypto.SHA384} - RegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod { - return SigningMethodRS384 - }) - - // RS512 - SigningMethodRS512 = &SigningMethodRSA{"RS512", crypto.SHA512} - RegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod { - return SigningMethodRS512 - }) -} - -func (m *SigningMethodRSA) Alg() string { - return m.Name -} - -// Implements the Verify method from SigningMethod -// For this signing method, must be an *rsa.PublicKey structure. -func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error { - var err error - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - - var rsaKey *rsa.PublicKey - var ok bool - - if rsaKey, ok = key.(*rsa.PublicKey); !ok { - return ErrInvalidKeyType - } - - // Create hasher - if !m.Hash.Available() { - return ErrHashUnavailable - } - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Verify the signature - return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig) -} - -// Implements the Sign method from SigningMethod -// For this signing method, must be an *rsa.PrivateKey structure. -func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) { - var rsaKey *rsa.PrivateKey - var ok bool - - // Validate type of key - if rsaKey, ok = key.(*rsa.PrivateKey); !ok { - return "", ErrInvalidKey - } - - // Create the hasher - if !m.Hash.Available() { - return "", ErrHashUnavailable - } - - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Sign the string and return the encoded bytes - if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil { - return EncodeSegment(sigBytes), nil - } else { - return "", err - } -} diff --git a/vendor/github.com/golang-jwt/jwt/rsa_pss.go b/vendor/github.com/golang-jwt/jwt/rsa_pss.go deleted file mode 100644 index c01470864..000000000 --- a/vendor/github.com/golang-jwt/jwt/rsa_pss.go +++ /dev/null @@ -1,142 +0,0 @@ -// +build go1.4 - -package jwt - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" -) - -// Implements the RSAPSS family of signing methods signing methods -type SigningMethodRSAPSS struct { - *SigningMethodRSA - Options *rsa.PSSOptions - // VerifyOptions is optional. If set overrides Options for rsa.VerifyPPS. - // Used to accept tokens signed with rsa.PSSSaltLengthAuto, what doesn't follow - // https://tools.ietf.org/html/rfc7518#section-3.5 but was used previously. - // See https://github.com/dgrijalva/jwt-go/issues/285#issuecomment-437451244 for details. - VerifyOptions *rsa.PSSOptions -} - -// Specific instances for RS/PS and company. -var ( - SigningMethodPS256 *SigningMethodRSAPSS - SigningMethodPS384 *SigningMethodRSAPSS - SigningMethodPS512 *SigningMethodRSAPSS -) - -func init() { - // PS256 - SigningMethodPS256 = &SigningMethodRSAPSS{ - SigningMethodRSA: &SigningMethodRSA{ - Name: "PS256", - Hash: crypto.SHA256, - }, - Options: &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthEqualsHash, - }, - VerifyOptions: &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - }, - } - RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod { - return SigningMethodPS256 - }) - - // PS384 - SigningMethodPS384 = &SigningMethodRSAPSS{ - SigningMethodRSA: &SigningMethodRSA{ - Name: "PS384", - Hash: crypto.SHA384, - }, - Options: &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthEqualsHash, - }, - VerifyOptions: &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - }, - } - RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod { - return SigningMethodPS384 - }) - - // PS512 - SigningMethodPS512 = &SigningMethodRSAPSS{ - SigningMethodRSA: &SigningMethodRSA{ - Name: "PS512", - Hash: crypto.SHA512, - }, - Options: &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthEqualsHash, - }, - VerifyOptions: &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - }, - } - RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod { - return SigningMethodPS512 - }) -} - -// Implements the Verify method from SigningMethod -// For this verify method, key must be an rsa.PublicKey struct -func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error { - var err error - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - - var rsaKey *rsa.PublicKey - switch k := key.(type) { - case *rsa.PublicKey: - rsaKey = k - default: - return ErrInvalidKey - } - - // Create hasher - if !m.Hash.Available() { - return ErrHashUnavailable - } - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - opts := m.Options - if m.VerifyOptions != nil { - opts = m.VerifyOptions - } - - return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, opts) -} - -// Implements the Sign method from SigningMethod -// For this signing method, key must be an rsa.PrivateKey struct -func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) { - var rsaKey *rsa.PrivateKey - - switch k := key.(type) { - case *rsa.PrivateKey: - rsaKey = k - default: - return "", ErrInvalidKeyType - } - - // Create the hasher - if !m.Hash.Available() { - return "", ErrHashUnavailable - } - - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Sign the string and return the encoded bytes - if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil { - return EncodeSegment(sigBytes), nil - } else { - return "", err - } -} diff --git a/vendor/github.com/golang-jwt/jwt/rsa_utils.go b/vendor/github.com/golang-jwt/jwt/rsa_utils.go deleted file mode 100644 index 14c78c292..000000000 --- a/vendor/github.com/golang-jwt/jwt/rsa_utils.go +++ /dev/null @@ -1,101 +0,0 @@ -package jwt - -import ( - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "errors" -) - -var ( - ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be a PEM encoded PKCS1 or PKCS8 key") - ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key") - ErrNotRSAPublicKey = errors.New("Key is not a valid RSA public key") -) - -// Parse PEM encoded PKCS1 or PKCS8 private key -func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - var parsedKey interface{} - if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { - if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { - return nil, err - } - } - - var pkey *rsa.PrivateKey - var ok bool - if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { - return nil, ErrNotRSAPrivateKey - } - - return pkey, nil -} - -// Parse PEM encoded PKCS1 or PKCS8 private key protected with password -func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - var parsedKey interface{} - - var blockDecrypted []byte - if blockDecrypted, err = x509.DecryptPEMBlock(block, []byte(password)); err != nil { - return nil, err - } - - if parsedKey, err = x509.ParsePKCS1PrivateKey(blockDecrypted); err != nil { - if parsedKey, err = x509.ParsePKCS8PrivateKey(blockDecrypted); err != nil { - return nil, err - } - } - - var pkey *rsa.PrivateKey - var ok bool - if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { - return nil, ErrNotRSAPrivateKey - } - - return pkey, nil -} - -// Parse PEM encoded PKCS1 or PKCS8 public key -func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - // Parse the key - var parsedKey interface{} - if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { - if cert, err := x509.ParseCertificate(block.Bytes); err == nil { - parsedKey = cert.PublicKey - } else { - return nil, err - } - } - - var pkey *rsa.PublicKey - var ok bool - if pkey, ok = parsedKey.(*rsa.PublicKey); !ok { - return nil, ErrNotRSAPublicKey - } - - return pkey, nil -} diff --git a/vendor/github.com/golang-jwt/jwt/signing_method.go b/vendor/github.com/golang-jwt/jwt/signing_method.go deleted file mode 100644 index ed1f212b2..000000000 --- a/vendor/github.com/golang-jwt/jwt/signing_method.go +++ /dev/null @@ -1,35 +0,0 @@ -package jwt - -import ( - "sync" -) - -var signingMethods = map[string]func() SigningMethod{} -var signingMethodLock = new(sync.RWMutex) - -// Implement SigningMethod to add new methods for signing or verifying tokens. -type SigningMethod interface { - Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid - Sign(signingString string, key interface{}) (string, error) // Returns encoded signature or error - Alg() string // returns the alg identifier for this method (example: 'HS256') -} - -// Register the "alg" name and a factory function for signing method. -// This is typically done during init() in the method's implementation -func RegisterSigningMethod(alg string, f func() SigningMethod) { - signingMethodLock.Lock() - defer signingMethodLock.Unlock() - - signingMethods[alg] = f -} - -// Get a signing method from an "alg" string -func GetSigningMethod(alg string) (method SigningMethod) { - signingMethodLock.RLock() - defer signingMethodLock.RUnlock() - - if methodF, ok := signingMethods[alg]; ok { - method = methodF() - } - return -} diff --git a/vendor/github.com/golang-jwt/jwt/token.go b/vendor/github.com/golang-jwt/jwt/token.go deleted file mode 100644 index 6b30ced12..000000000 --- a/vendor/github.com/golang-jwt/jwt/token.go +++ /dev/null @@ -1,104 +0,0 @@ -package jwt - -import ( - "encoding/base64" - "encoding/json" - "strings" - "time" -) - -// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time). -// You can override it to use another time value. This is useful for testing or if your -// server uses a different time zone than your tokens. -var TimeFunc = time.Now - -// Parse methods use this callback function to supply -// the key for verification. The function receives the parsed, -// but unverified Token. This allows you to use properties in the -// Header of the token (such as `kid`) to identify which key to use. -type Keyfunc func(*Token) (interface{}, error) - -// A JWT Token. Different fields will be used depending on whether you're -// creating or parsing/verifying a token. -type Token struct { - Raw string // The raw token. Populated when you Parse a token - Method SigningMethod // The signing method used or to be used - Header map[string]interface{} // The first segment of the token - Claims Claims // The second segment of the token - Signature string // The third segment of the token. Populated when you Parse a token - Valid bool // Is the token valid? Populated when you Parse/Verify a token -} - -// Create a new Token. Takes a signing method -func New(method SigningMethod) *Token { - return NewWithClaims(method, MapClaims{}) -} - -func NewWithClaims(method SigningMethod, claims Claims) *Token { - return &Token{ - Header: map[string]interface{}{ - "typ": "JWT", - "alg": method.Alg(), - }, - Claims: claims, - Method: method, - } -} - -// Get the complete, signed token -func (t *Token) SignedString(key interface{}) (string, error) { - var sig, sstr string - var err error - if sstr, err = t.SigningString(); err != nil { - return "", err - } - if sig, err = t.Method.Sign(sstr, key); err != nil { - return "", err - } - return strings.Join([]string{sstr, sig}, "."), nil -} - -// Generate the signing string. This is the -// most expensive part of the whole deal. Unless you -// need this for something special, just go straight for -// the SignedString. -func (t *Token) SigningString() (string, error) { - var err error - parts := make([]string, 2) - for i := range parts { - var jsonValue []byte - if i == 0 { - if jsonValue, err = json.Marshal(t.Header); err != nil { - return "", err - } - } else { - if jsonValue, err = json.Marshal(t.Claims); err != nil { - return "", err - } - } - - parts[i] = EncodeSegment(jsonValue) - } - return strings.Join(parts, "."), nil -} - -// Parse, validate, and return a token. -// keyFunc will receive the parsed token and should return the key for validating. -// If everything is kosher, err will be nil -func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { - return new(Parser).Parse(tokenString, keyFunc) -} - -func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { - return new(Parser).ParseWithClaims(tokenString, claims, keyFunc) -} - -// Encode JWT specific base64url encoding with padding stripped -func EncodeSegment(seg []byte) string { - return base64.RawURLEncoding.EncodeToString(seg) -} - -// Decode JWT specific base64url encoding with padding stripped -func DecodeSegment(seg string) ([]byte, error) { - return base64.RawURLEncoding.DecodeString(seg) -} diff --git a/vendor/github.com/josharian/intern/README.md b/vendor/github.com/josharian/intern/README.md deleted file mode 100644 index ffc44b219..000000000 --- a/vendor/github.com/josharian/intern/README.md +++ /dev/null @@ -1,5 +0,0 @@ -Docs: https://godoc.org/github.com/josharian/intern - -See also [Go issue 5160](https://golang.org/issue/5160). - -License: MIT diff --git a/vendor/github.com/josharian/intern/intern.go b/vendor/github.com/josharian/intern/intern.go deleted file mode 100644 index 7acb1fe90..000000000 --- a/vendor/github.com/josharian/intern/intern.go +++ /dev/null @@ -1,44 +0,0 @@ -// Package intern interns strings. -// Interning is best effort only. -// Interned strings may be removed automatically -// at any time without notification. -// All functions may be called concurrently -// with themselves and each other. -package intern - -import "sync" - -var ( - pool sync.Pool = sync.Pool{ - New: func() interface{} { - return make(map[string]string) - }, - } -) - -// String returns s, interned. -func String(s string) string { - m := pool.Get().(map[string]string) - c, ok := m[s] - if ok { - pool.Put(m) - return c - } - m[s] = s - pool.Put(m) - return s -} - -// Bytes returns b converted to a string, interned. -func Bytes(b []byte) string { - m := pool.Get().(map[string]string) - c, ok := m[string(b)] - if ok { - pool.Put(m) - return c - } - s := string(b) - m[s] = s - pool.Put(m) - return s -} diff --git a/vendor/github.com/josharian/intern/license.md b/vendor/github.com/josharian/intern/license.md deleted file mode 100644 index 353d3055f..000000000 --- a/vendor/github.com/josharian/intern/license.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Josh Bleecher Snyder - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/labstack/echo/v4/CHANGELOG.md b/vendor/github.com/labstack/echo/v4/CHANGELOG.md index de3857fff..8388531e9 100644 --- a/vendor/github.com/labstack/echo/v4/CHANGELOG.md +++ b/vendor/github.com/labstack/echo/v4/CHANGELOG.md @@ -1,5 +1,367 @@ # Changelog +## v4.15.4 - 2026-06-15 + +**Security** + +Fixes [GHSA-vfp3-v2gw-7wfq](https://github.com/labstack/echo/security/advisories/GHSA-vfp3-v2gw-7wfq) + +Make serving static file releated methods and middleware not unescape path by default - so how the way Router interprets paths and Static methods/middleware is consistent. + +Given following situation: +```go +// 0. +// given folder structure: +// private.txt +// public/ +// public/index.html +// public/text.txt +// public/admin/private.txt + +// 1. share `public/` folder contents from the server root. This folder actually contains subfolder `admin` which +// contents we want to forbid from downloading +e.Static("/", "public") + +// 2. naively assume that everything under /admin folder is now forbidden +e.GET("/admin/*", func(c *Context) error { + return ErrForbidden +}) +``` + +Then requests to `/admin%2fprivate.txt` would not be matched to `GET /admin/*` route (routing does not look unescaped path) and static file serving will use unescaped path to serve the file. + +Note: this way of "guarding" subfolders will never work for for paths like `/assets/../admin%2fprivate.txt` which will `path.Clean("/assets/../admin%2fprivate.txt")` to `/admin/private.txt` and are servable if static file serving is configured to unescape paths. + +If you want to guard routes - use middlewares on `Static*` methods and before `Static` middleware. + +**Breaking change / migration:** If you serve files whose names contain URL-encoded characters (e.g., `/hello%20world.txt` → `hello world.txt`), you must now opt in: + +```go + e := echo.New() + e.EnablePathUnescapingStaticFiles = true // <-- enable old behavior + e.Static("/", "public") +``` +for static middleware +```go + e.Use(middleware.StaticWithConfig(middleware.StaticConfig{ + EnablePathUnescaping: true, // <-- enable old behavior + })) +``` + + +## v4.15.3 - 2026-06-14 + +**Security** + +* fix(static): reject encoded path separators that bypass route-level middleware by @vishr in https://github.com/labstack/echo/pull/3011 + +Fixes [GHSA-vfp3-v2gw-7wfq](https://github.com/labstack/echo/security/advisories/GHSA-vfp3-v2gw-7wfq): an encoded path separator (`%2F` or `%5C`) in a static file URL could bypass route-level middleware (e.g. authentication on a sibling route) and disclose static files. Both `StaticDirectoryHandler` (used by `Static`/`StaticFS`) and the `Static` middleware are affected. Backport of the v5 fix (#3009). Thanks to @a-tt-om and @oran-gugu for reporting. + + +## v4.15.2 - 2026-05-01 + +**Security** + +* `Context.Scheme()` should validate values taken from header by @aldas in https://github.com/labstack/echo/pull/2962 + +Thanks to @shblue21 for reporting this [issue](https://github.com/labstack/echo/issues/2952). + + +## v4.15.1 - 2026-02-22 + +**Enhancements** + +* CSRF: support older token-based CSRF protection handler that want to render token into template by @aldas in https://github.com/labstack/echo/pull/2905 + + +## v4.15.0 - 2026-01-01 + + +**Security** + +NB: **If your application relies on cross-origin or same-site (same subdomain) requests do not blindly push this version to production** + + +The CSRF middleware now supports the [**Sec-Fetch-Site**](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site) header as a modern, defense-in-depth approach to [CSRF +protection](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#fetch-metadata-headers), implementing the OWASP-recommended Fetch Metadata API alongside the traditional token-based mechanism. + +**How it works:** + +Modern browsers automatically send the `Sec-Fetch-Site` header with all requests, indicating the relationship +between the request origin and the target. The middleware uses this to make security decisions: + +- **`same-origin`** or **`none`**: Requests are allowed (exact origin match or direct user navigation) +- **`same-site`**: Falls back to token validation (e.g., subdomain to main domain) +- **`cross-site`**: Blocked by default with 403 error for unsafe methods (POST, PUT, DELETE, PATCH) + +For browsers that don't send this header (older browsers), the middleware seamlessly falls back to +traditional token-based CSRF protection. + +**New Configuration Options:** +- `TrustedOrigins []string`: Allowlist specific origins for cross-site requests (useful for OAuth callbacks, webhooks) +- `AllowSecFetchSiteFunc func(echo.Context) (bool, error)`: Custom logic for same-site/cross-site request validation + +**Example:** + ```go + e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{ + // Allow OAuth callbacks from trusted provider + TrustedOrigins: []string{"https://oauth-provider.com"}, + + // Custom validation for same-site requests + AllowSecFetchSiteFunc: func(c echo.Context) (bool, error) { + // Your custom authorization logic here + return validateCustomAuth(c), nil + // return true, err // blocks request with error + // return true, nil // allows CSRF request through + // return false, nil // falls back to legacy token logic + }, + })) + ``` +PR: https://github.com/labstack/echo/pull/2858 + +**Type-Safe Generic Parameter Binding** + +* Added generic functions for type-safe parameter extraction and context access by @aldas in https://github.com/labstack/echo/pull/2856 + + Echo now provides generic functions for extracting path, query, and form parameters with automatic type conversion, + eliminating manual string parsing and type assertions. + + **New Functions:** + - Path parameters: `PathParam[T]`, `PathParamOr[T]` + - Query parameters: `QueryParam[T]`, `QueryParamOr[T]`, `QueryParams[T]`, `QueryParamsOr[T]` + - Form values: `FormParam[T]`, `FormParamOr[T]`, `FormParams[T]`, `FormParamsOr[T]` + - Context store: `ContextGet[T]`, `ContextGetOr[T]` + + **Supported Types:** + Primitives (`bool`, `string`, `int`/`uint` variants, `float32`/`float64`), `time.Duration`, `time.Time` + (with custom layouts and Unix timestamp support), and custom types implementing `BindUnmarshaler`, + `TextUnmarshaler`, or `JSONUnmarshaler`. + + **Example:** + ```go + // Before: Manual parsing + idStr := c.Param("id") + id, err := strconv.Atoi(idStr) + + // After: Type-safe with automatic parsing + id, err := echo.PathParam[int](c, "id") + + // With default values + page, err := echo.QueryParamOr[int](c, "page", 1) + limit, err := echo.QueryParamOr[int](c, "limit", 20) + + // Type-safe context access (no more panics from type assertions) + user, err := echo.ContextGet[*User](c, "user") + ``` + +PR: https://github.com/labstack/echo/pull/2856 + + + +**DEPRECATION NOTICE** Timeout Middleware Deprecated - Use ContextTimeout Instead + +The `middleware.Timeout` middleware has been **deprecated** due to fundamental architectural issues that cause +data races. Use `middleware.ContextTimeout` or `middleware.ContextTimeoutWithConfig` instead. + +**Why is this being deprecated?** + +The Timeout middleware manipulates response writers across goroutine boundaries, which causes data races that +cannot be reliably fixed without a complete architectural redesign. The middleware: + +- Swaps the response writer using `http.TimeoutHandler` +- Must be the first middleware in the chain (fragile constraint) +- Can cause races with other middleware (Logger, metrics, custom middleware) +- Has been the source of multiple race condition fixes over the years + +**What should you use instead?** + +The `ContextTimeout` middleware (available since v4.12.0) provides timeout functionality using Go's standard +context mechanism. It is: + +- Race-free by design +- Can be placed anywhere in the middleware chain +- Simpler and more maintainable +- Compatible with all other middleware + +**Migration Guide:** + +```go +// Before (deprecated): +e.Use(middleware.Timeout()) + +// After (recommended): +e.Use(middleware.ContextTimeout(30 * time.Second)) +``` + +**Important Behavioral Differences:** + +1. **Handler cooperation required**: With ContextTimeout, your handlers must check `context.Done()` for cooperative + cancellation. The old Timeout middleware would send a 503 response regardless of handler cooperation, but had + data race issues. + +2. **Error handling**: ContextTimeout returns errors through the standard error handling flow. Handlers that receive + `context.DeadlineExceeded` should handle it appropriately: + +```go +e.GET("/long-task", func(c echo.Context) error { + ctx := c.Request().Context() + + // Example: database query with context + result, err := db.QueryContext(ctx, "SELECT * FROM large_table") + if err != nil { + if errors.Is(err, context.DeadlineExceeded) { + // Handle timeout + return echo.NewHTTPError(http.StatusServiceUnavailable, "Request timeout") + } + return err + } + + return c.JSON(http.StatusOK, result) +}) +``` + +3. **Background tasks**: For long-running background tasks, use goroutines with context: + +```go +e.GET("/async-task", func(c echo.Context) error { + ctx := c.Request().Context() + + resultCh := make(chan Result, 1) + errCh := make(chan error, 1) + + go func() { + result, err := performLongTask(ctx) + if err != nil { + errCh <- err + return + } + resultCh <- result + }() + + select { + case result := <-resultCh: + return c.JSON(http.StatusOK, result) + case err := <-errCh: + return err + case <-ctx.Done(): + return echo.NewHTTPError(http.StatusServiceUnavailable, "Request timeout") + } +}) +``` + +**Enhancements** + +* Fixes by @aldas in https://github.com/labstack/echo/pull/2852 +* Generic functions by @aldas in https://github.com/labstack/echo/pull/2856 +* CRSF with Sec-Fetch-Site checks by @aldas in https://github.com/labstack/echo/pull/2858 + + +## v4.14.0 - 2025-12-11 + +`middleware.Logger` has been deprecated. For request logging, use `middleware.RequestLogger` or +`middleware.RequestLoggerWithConfig`. + +`middleware.RequestLogger` replaces `middleware.Logger`, offering comparable configuration while relying on the +Go standard library’s new `slog` logger. + +The previous default output format was JSON. The new default follows the standard `slog` logger settings. +To continue emitting request logs in JSON, configure `slog` accordingly: +```go +slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil))) +e.Use(middleware.RequestLogger()) +``` + + +**Security** + +* Logger middleware json string escaping and deprecation by @aldas in https://github.com/labstack/echo/pull/2849 + + + +**Enhancements** + +* Update deps by @aldas in https://github.com/labstack/echo/pull/2807 +* refactor to use reflect.TypeFor by @cuiweixie in https://github.com/labstack/echo/pull/2812 +* Use Go 1.25 in CI by @aldas in https://github.com/labstack/echo/pull/2810 +* Modernize context.go by replacing interface{} with any by @vishr in https://github.com/labstack/echo/pull/2822 +* Fix typo in SetParamValues comment by @vishr in https://github.com/labstack/echo/pull/2828 +* Fix typo in ContextTimeout middleware comment by @vishr in https://github.com/labstack/echo/pull/2827 +* Improve BasicAuth middleware: use strings.Cut and RFC compliance by @vishr in https://github.com/labstack/echo/pull/2825 +* Fix duplicate plus operator in router backtracking logic by @yuya-morimoto in https://github.com/labstack/echo/pull/2832 +* Replace custom private IP range check with built-in net.IP.IsPrivate by @kumapower17 in https://github.com/labstack/echo/pull/2835 +* Ensure proxy connection is closed in proxyRaw function(#2837) by @kumapower17 in https://github.com/labstack/echo/pull/2838 +* Update deps by @aldas in https://github.com/labstack/echo/pull/2843 +* Update golang.org/x/* deps by @aldas in https://github.com/labstack/echo/pull/2850 + + + +## v4.13.4 - 2025-05-22 + +**Enhancements** + +* chore: fix some typos in comment by @zhuhaicity in https://github.com/labstack/echo/pull/2735 +* CI: test with Go 1.24 by @aldas in https://github.com/labstack/echo/pull/2748 +* Add support for TLS WebSocket proxy by @t-ibayashi-safie in https://github.com/labstack/echo/pull/2762 + +**Security** + +* Update dependencies for [GO-2025-3487](https://pkg.go.dev/vuln/GO-2025-3487), [GO-2025-3503](https://pkg.go.dev/vuln/GO-2025-3503) and [GO-2025-3595](https://pkg.go.dev/vuln/GO-2025-3595) in https://github.com/labstack/echo/pull/2780 + + +## v4.13.3 - 2024-12-19 + +**Security** + +* Update golang.org/x/net dependency [GO-2024-3333](https://pkg.go.dev/vuln/GO-2024-3333) in https://github.com/labstack/echo/pull/2722 + + +## v4.13.2 - 2024-12-12 + +**Security** + +* Update dependencies (dependabot reports [GO-2024-3321](https://pkg.go.dev/vuln/GO-2024-3321)) in https://github.com/labstack/echo/pull/2721 + + +## v4.13.1 - 2024-12-11 + +**Fixes** + +* Fix BindBody ignoring `Transfer-Encoding: chunked` requests by @178inaba in https://github.com/labstack/echo/pull/2717 + + + +## v4.13.0 - 2024-12-04 + +**BREAKING CHANGE** JWT Middleware Removed from Core use [labstack/echo-jwt](https://github.com/labstack/echo-jwt) instead + +The JWT middleware has been **removed from Echo core** due to another security vulnerability, [CVE-2024-51744](https://nvd.nist.gov/vuln/detail/CVE-2024-51744). For more details, refer to issue [#2699](https://github.com/labstack/echo/issues/2699). A drop-in replacement is available in the [labstack/echo-jwt](https://github.com/labstack/echo-jwt) repository. + +**Important**: Direct assignments like `token := c.Get("user").(*jwt.Token)` will now cause a panic due to an invalid cast. Update your code accordingly. Replace the current imports from `"github.com/golang-jwt/jwt"` in your handlers to the new middleware version using `"github.com/golang-jwt/jwt/v5"`. + + +Background: + +The version of `golang-jwt/jwt` (v3.2.2) previously used in Echo core has been in an unmaintained state for some time. This is not the first vulnerability affecting this library; earlier issues were addressed in [PR #1946](https://github.com/labstack/echo/pull/1946). +JWT middleware was marked as deprecated in Echo core as of [v4.10.0](https://github.com/labstack/echo/releases/tag/v4.10.0) on 2022-12-27. If you did not notice that, consider leveraging tools like [Staticcheck](https://staticcheck.dev/) to catch such deprecations earlier in you dev/CI flow. For bonus points - check out [gosec](https://github.com/securego/gosec). + +We sincerely apologize for any inconvenience caused by this change. While we strive to maintain backward compatibility within Echo core, recurring security issues with third-party dependencies have forced this decision. + +**Enhancements** + +* remove jwt middleware by @stevenwhitehead in https://github.com/labstack/echo/pull/2701 +* optimization: struct alignment by @behnambm in https://github.com/labstack/echo/pull/2636 +* bind: Maintain backwards compatibility for map[string]interface{} binding by @thesaltree in https://github.com/labstack/echo/pull/2656 +* Add Go 1.23 to CI by @aldas in https://github.com/labstack/echo/pull/2675 +* improve `MultipartForm` test by @martinyonatann in https://github.com/labstack/echo/pull/2682 +* `bind` : add support of multipart multi files by @martinyonatann in https://github.com/labstack/echo/pull/2684 +* Add TemplateRenderer struct to ease creating renderers for `html/template` and `text/template` packages. by @aldas in https://github.com/labstack/echo/pull/2690 +* Refactor TestBasicAuth to utilize table-driven test format by @ErikOlson in https://github.com/labstack/echo/pull/2688 +* Remove broken header by @aldas in https://github.com/labstack/echo/pull/2705 +* fix(bind body): content-length can be -1 by @phamvinhdat in https://github.com/labstack/echo/pull/2710 +* CORS middleware should compile allowOrigin regexp at creation by @aldas in https://github.com/labstack/echo/pull/2709 +* Shorten Github issue template and add test example by @aldas in https://github.com/labstack/echo/pull/2711 + + ## v4.12.0 - 2024-04-15 **Security** diff --git a/vendor/github.com/labstack/echo/v4/CLAUDE.md b/vendor/github.com/labstack/echo/v4/CLAUDE.md new file mode 100644 index 000000000..decbf0792 --- /dev/null +++ b/vendor/github.com/labstack/echo/v4/CLAUDE.md @@ -0,0 +1,99 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## About This Project + +Echo is a high performance, minimalist Go web framework. This is the main repository for Echo v4, which is available as a Go module at `github.com/labstack/echo/v4`. + +## Development Commands + +The project uses a Makefile for common development tasks: + +- `make check` - Run linting, vetting, and race condition tests (default target) +- `make init` - Install required linting tools (golint, staticcheck) +- `make lint` - Run staticcheck and golint +- `make vet` - Run go vet +- `make test` - Run short tests +- `make race` - Run tests with race detector +- `make benchmark` - Run benchmarks + +Example commands for development: +```bash +# Setup development environment +make init + +# Run all checks (lint, vet, race) +make check + +# Run specific tests +go test ./middleware/... +go test -race ./... + +# Run benchmarks +make benchmark +``` + +## Code Architecture + +### Core Components + +**Echo Instance (`echo.go`)** +- The `Echo` struct is the top-level framework instance +- Contains router, middleware stacks, and server configuration +- Not goroutine-safe for mutations after server start + +**Context (`context.go`)** +- The `Context` interface represents HTTP request/response context +- Provides methods for request/response handling, path parameters, data binding +- Core abstraction for request processing + +**Router (`router.go`)** +- Radix tree-based HTTP router with smart route prioritization +- Supports static routes, parameterized routes (`/users/:id`), and wildcard routes (`/static/*`) +- Each HTTP method has its own routing tree + +**Middleware (`middleware/`)** +- Extensive middleware system with 50+ built-in middlewares +- Middleware can be applied at Echo, Group, or individual route level +- Common middleware: Logger, Recover, CORS, JWT, Rate Limiting, etc. + +### Key Patterns + +**Middleware Chain** +- Pre-middleware runs before routing +- Regular middleware runs after routing but before handlers +- Middleware functions have signature `func(next echo.HandlerFunc) echo.HandlerFunc` + +**Route Groups** +- Routes can be grouped with common prefixes and middleware +- Groups support nested sub-groups +- Defined in `group.go` + +**Data Binding** +- Automatic binding of request data (JSON, XML, form) to Go structs +- Implemented in `binder.go` with support for custom binders + +**Error Handling** +- Centralized error handling via `HTTPErrorHandler` +- Automatic panic recovery with stack traces + +## File Organization + +- Root directory: Core Echo functionality (echo.go, context.go, router.go, etc.) +- `middleware/`: All built-in middleware implementations +- `_test/`: Test fixtures and utilities +- `_fixture/`: Test data files + +## Code Style + +- Go code uses tabs for indentation (per .editorconfig) +- Follows standard Go conventions and formatting +- Uses gofmt, golint, and staticcheck for code quality + +## Testing + +- Standard Go testing with `testing` package +- Tests include unit tests, integration tests, and benchmarks +- Race condition testing is required (`make race`) +- Test files follow `*_test.go` naming convention \ No newline at end of file diff --git a/vendor/github.com/labstack/echo/v4/Makefile b/vendor/github.com/labstack/echo/v4/Makefile index f9e5afb09..cbd78f1bf 100644 --- a/vendor/github.com/labstack/echo/v4/Makefile +++ b/vendor/github.com/labstack/echo/v4/Makefile @@ -31,6 +31,7 @@ benchmark: ## Run benchmarks help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' -goversion ?= "1.19" -test_version: ## Run tests inside Docker with given version (defaults to 1.19 oldest supported). Example: make test_version goversion=1.19 - @docker run --rm -it -v $(shell pwd):/project golang:$(goversion) /bin/sh -c "cd /project && make init check" +goversion ?= "1.22" +docker_user ?= "1000" +test_version: ## Run tests inside Docker with given version (defaults to 1.22 oldest supported). Example: make test_version goversion=1.22 + @docker run --rm -it --user $(docker_user) -e HOME=/tmp -e GOCACHE=/tmp/go-cache -v $(shell pwd):/project golang:$(goversion) /bin/sh -c "mkdir -p /tmp/go-cache /tmp/.cache && cd /project && make init check" diff --git a/vendor/github.com/labstack/echo/v4/README.md b/vendor/github.com/labstack/echo/v4/README.md index 351ba3c55..5e52d1d4e 100644 --- a/vendor/github.com/labstack/echo/v4/README.md +++ b/vendor/github.com/labstack/echo/v4/README.md @@ -1,5 +1,3 @@ - - [![Sourcegraph](https://sourcegraph.com/github.com/labstack/echo/-/badge.svg?style=flat-square)](https://sourcegraph.com/github.com/labstack/echo?badge) [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/labstack/echo/v4) [![Go Report Card](https://goreportcard.com/badge/github.com/labstack/echo?style=flat-square)](https://goreportcard.com/report/github.com/labstack/echo) @@ -48,17 +46,6 @@ Help and questions: [Github Discussions](https://github.com/labstack/echo/discus Click [here](https://github.com/sponsors/labstack) for more information on sponsorship. -## Benchmarks - -Date: 2020/11/11
-Source: https://github.com/vishr/web-framework-benchmark
-Lower is better! - - - - -The benchmarks above were run on an Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz - ## [Guide](https://echo.labstack.com/guide) ### Installation @@ -77,6 +64,7 @@ package main import ( "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" + "log/slog" "net/http" ) @@ -85,14 +73,16 @@ func main() { e := echo.New() // Middleware - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) + e.Use(middleware.RequestLogger()) // use the default RequestLogger middleware with slog logger + e.Use(middleware.Recover()) // recover panics as errors for proper error handling // Routes e.GET("/", hello) // Start server - e.Logger.Fatal(e.Start(":1323")) + if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) { + slog.Error("failed to start server", "error", err) + } } // Handler diff --git a/vendor/github.com/labstack/echo/v4/bind.go b/vendor/github.com/labstack/echo/v4/bind.go index 507def3ea..1d4fe6f0a 100644 --- a/vendor/github.com/labstack/echo/v4/bind.go +++ b/vendor/github.com/labstack/echo/v4/bind.go @@ -8,10 +8,12 @@ import ( "encoding/xml" "errors" "fmt" + "mime/multipart" "net/http" "reflect" "strconv" "strings" + "time" ) // Binder is the interface that wraps the Bind method. @@ -38,6 +40,13 @@ type bindMultipleUnmarshaler interface { } // BindPathParams binds path params to bindable object +// +// Time format support: time.Time fields can use `format` tags to specify custom parsing layouts. +// Example: `param:"created" format:"2006-01-02T15:04"` for datetime-local format +// Example: `param:"date" format:"2006-01-02"` for date format +// Uses Go's standard time format reference time: Mon Jan 2 15:04:05 MST 2006 +// Works with form data, query parameters, and path parameters (not JSON body) +// Falls back to default time.Time parsing if no format tag is specified func (b *DefaultBinder) BindPathParams(c Context, i interface{}) error { names := c.ParamNames() values := c.ParamValues() @@ -45,7 +54,7 @@ func (b *DefaultBinder) BindPathParams(c Context, i interface{}) error { for i, name := range names { params[name] = []string{values[i]} } - if err := b.bindData(i, params, "param"); err != nil { + if err := b.bindData(i, params, "param", nil); err != nil { return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) } return nil @@ -53,7 +62,7 @@ func (b *DefaultBinder) BindPathParams(c Context, i interface{}) error { // BindQueryParams binds query params to bindable object func (b *DefaultBinder) BindQueryParams(c Context, i interface{}) error { - if err := b.bindData(i, c.QueryParams(), "query"); err != nil { + if err := b.bindData(i, c.QueryParams(), "query", nil); err != nil { return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) } return nil @@ -70,9 +79,12 @@ func (b *DefaultBinder) BindBody(c Context, i interface{}) (err error) { return } - ctype := req.Header.Get(HeaderContentType) - switch { - case strings.HasPrefix(ctype, MIMEApplicationJSON): + // mediatype is found like `mime.ParseMediaType()` does it + base, _, _ := strings.Cut(req.Header.Get(HeaderContentType), ";") + mediatype := strings.TrimSpace(base) + + switch mediatype { + case MIMEApplicationJSON: if err = c.Echo().JSONSerializer.Deserialize(c, i); err != nil { switch err.(type) { case *HTTPError: @@ -81,7 +93,7 @@ func (b *DefaultBinder) BindBody(c Context, i interface{}) (err error) { return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) } } - case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML): + case MIMEApplicationXML, MIMETextXML: if err = xml.NewDecoder(req.Body).Decode(i); err != nil { if ute, ok := err.(*xml.UnsupportedTypeError); ok { return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error())).SetInternal(err) @@ -90,12 +102,20 @@ func (b *DefaultBinder) BindBody(c Context, i interface{}) (err error) { } return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) } - case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm): + case MIMEApplicationForm: params, err := c.FormParams() if err != nil { return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) } - if err = b.bindData(i, params, "form"); err != nil { + if err = b.bindData(i, params, "form", nil); err != nil { + return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) + } + case MIMEMultipartForm: + params, err := c.MultipartForm() + if err != nil { + return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) + } + if err = b.bindData(i, params.Value, "form", params.File); err != nil { return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) } default: @@ -106,7 +126,7 @@ func (b *DefaultBinder) BindBody(c Context, i interface{}) (err error) { // BindHeaders binds HTTP headers to a bindable object func (b *DefaultBinder) BindHeaders(c Context, i interface{}) error { - if err := b.bindData(i, c.Request().Header, "header"); err != nil { + if err := b.bindData(i, c.Request().Header, "header", nil); err != nil { return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) } return nil @@ -132,10 +152,11 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) { } // bindData will bind data ONLY fields in destination struct that have EXPLICIT tag -func (b *DefaultBinder) bindData(destination interface{}, data map[string][]string, tag string) error { - if destination == nil || len(data) == 0 { +func (b *DefaultBinder) bindData(destination interface{}, data map[string][]string, tag string, dataFiles map[string][]*multipart.FileHeader) error { + if destination == nil || (len(data) == 0 && len(dataFiles) == 0) { return nil } + hasFiles := len(dataFiles) > 0 typ := reflect.TypeOf(destination).Elem() val := reflect.ValueOf(destination).Elem() @@ -159,6 +180,10 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri for k, v := range data { if isElemString { val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) + } else if isElemInterface { + // To maintain backward compatibility, we always bind to the first string value + // and not the slice of strings when dealing with map[string]interface{}{} + val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) } else { val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v)) } @@ -175,7 +200,7 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri return errors.New("binding element must be a struct") } - for i := 0; i < typ.NumField(); i++ { + for i := 0; i < typ.NumField(); i++ { // iterate over all destination fields typeField := typ.Field(i) structField := val.Field(i) if typeField.Anonymous { @@ -194,10 +219,10 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri } if inputFieldName == "" { - // If tag is nil, we inspect if the field is a not BindUnmarshaler struct and try to bind data into it (might contains fields with tags). + // If tag is nil, we inspect if the field is a not BindUnmarshaler struct and try to bind data into it (might contain fields with tags). // structs that implement BindUnmarshaler are bound only when they have explicit tag if _, ok := structField.Addr().Interface().(BindUnmarshaler); !ok && structFieldKind == reflect.Struct { - if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil { + if err := b.bindData(structField.Addr().Interface(), data, tag, dataFiles); err != nil { return err } } @@ -205,10 +230,20 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri continue } + if hasFiles { + if ok, err := isFieldMultipartFile(structField.Type()); err != nil { + return err + } else if ok { + if ok := setMultipartFileHeaderTypes(structField, inputFieldName, dataFiles); ok { + continue + } + } + } + inputValue, exists := data[inputFieldName] if !exists { - // Go json.Unmarshal supports case insensitive binding. However the - // url params are bound case sensitive which is inconsistent. To + // Go json.Unmarshal supports case-insensitive binding. However the + // url params are bound case-sensitive which is inconsistent. To // fix this we must check all of the map values in a // case-insensitive search. for k, v := range data { @@ -235,14 +270,15 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri continue } - if ok, err := unmarshalInputToField(typeField.Type.Kind(), inputValue[0], structField); ok { + formatTag := typeField.Tag.Get("format") + if ok, err := unmarshalInputToField(typeField.Type.Kind(), inputValue[0], structField, formatTag); ok { if err != nil { return err } continue } - // we could be dealing with pointer to slice `*[]string` so dereference it. There are wierd OpenAPI generators + // we could be dealing with pointer to slice `*[]string` so dereference it. There are weird OpenAPI generators // that could create struct fields like that. if structFieldKind == reflect.Pointer { structFieldKind = structField.Elem().Kind() @@ -271,7 +307,8 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error { // But also call it here, in case we're dealing with an array of BindUnmarshalers - if ok, err := unmarshalInputToField(valueKind, val, structField); ok { + // Note: format tag not available in this context, so empty string is passed + if ok, err := unmarshalInputToField(valueKind, val, structField, ""); ok { return err } @@ -328,7 +365,7 @@ func unmarshalInputsToField(valueKind reflect.Kind, values []string, field refle return true, unmarshaler.UnmarshalParams(values) } -func unmarshalInputToField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) { +func unmarshalInputToField(valueKind reflect.Kind, val string, field reflect.Value, formatTag string) (bool, error) { if valueKind == reflect.Ptr { if field.IsNil() { field.Set(reflect.New(field.Type().Elem())) @@ -337,6 +374,19 @@ func unmarshalInputToField(valueKind reflect.Kind, val string, field reflect.Val } fieldIValue := field.Addr().Interface() + + // Handle time.Time with custom format tag + if formatTag != "" { + if _, isTime := fieldIValue.(*time.Time); isTime { + t, err := time.Parse(formatTag, val) + if err != nil { + return true, err + } + field.Set(reflect.ValueOf(t)) + return true, nil + } + } + switch unmarshaler := fieldIValue.(type) { case BindUnmarshaler: return true, unmarshaler.UnmarshalParam(val) @@ -390,3 +440,50 @@ func setFloatField(value string, bitSize int, field reflect.Value) error { } return err } + +var ( + // NOT supported by bind as you can NOT check easily empty struct being actual file or not + multipartFileHeaderType = reflect.TypeFor[multipart.FileHeader]() + // supported by bind as you can check by nil value if file existed or not + multipartFileHeaderPointerType = reflect.TypeFor[*multipart.FileHeader]() + multipartFileHeaderSliceType = reflect.TypeFor[[]multipart.FileHeader]() + multipartFileHeaderPointerSliceType = reflect.TypeFor[[]*multipart.FileHeader]() +) + +func isFieldMultipartFile(field reflect.Type) (bool, error) { + switch field { + case multipartFileHeaderPointerType, + multipartFileHeaderSliceType, + multipartFileHeaderPointerSliceType: + return true, nil + case multipartFileHeaderType: + return true, errors.New("binding to multipart.FileHeader struct is not supported, use pointer to struct") + default: + return false, nil + } +} + +func setMultipartFileHeaderTypes(structField reflect.Value, inputFieldName string, files map[string][]*multipart.FileHeader) bool { + fileHeaders := files[inputFieldName] + if len(fileHeaders) == 0 { + return false + } + + result := true + switch structField.Type() { + case multipartFileHeaderPointerSliceType: + structField.Set(reflect.ValueOf(fileHeaders)) + case multipartFileHeaderSliceType: + headers := make([]multipart.FileHeader, len(fileHeaders)) + for i, fileHeader := range fileHeaders { + headers[i] = *fileHeader + } + structField.Set(reflect.ValueOf(headers)) + case multipartFileHeaderPointerType: + structField.Set(reflect.ValueOf(fileHeaders[0])) + default: + result = false + } + + return result +} diff --git a/vendor/github.com/labstack/echo/v4/binder.go b/vendor/github.com/labstack/echo/v4/binder.go index ebabeaf96..da15ae82a 100644 --- a/vendor/github.com/labstack/echo/v4/binder.go +++ b/vendor/github.com/labstack/echo/v4/binder.go @@ -69,9 +69,9 @@ import ( type BindingError struct { // Field is the field name where value binding failed Field string `json:"field"` + *HTTPError // Values of parameter that failed to bind. Values []string `json:"-"` - *HTTPError } // NewBindingError creates new instance of binding error @@ -94,16 +94,15 @@ func (be *BindingError) Error() string { // ValueBinder provides utility methods for binding query or path parameter to various Go built-in types type ValueBinder struct { - // failFast is flag for binding methods to return without attempting to bind when previous binding already failed - failFast bool - errors []error - // ValueFunc is used to get single parameter (first) value from request ValueFunc func(sourceParam string) string // ValuesFunc is used to get all values for parameter from request. i.e. `/api/search?ids=1&ids=2` ValuesFunc func(sourceParam string) []string // ErrorFunc is used to create errors. Allows you to use your own error type, that for example marshals to your specific json response ErrorFunc func(sourceParam string, values []string, message interface{}, internalError error) error + errors []error + // failFast is flag for binding methods to return without attempting to bind when previous binding already failed + failFast bool } // QueryParamsBinder creates query parameter value binder diff --git a/vendor/github.com/labstack/echo/v4/binder_generic.go b/vendor/github.com/labstack/echo/v4/binder_generic.go new file mode 100644 index 000000000..f4d45af76 --- /dev/null +++ b/vendor/github.com/labstack/echo/v4/binder_generic.go @@ -0,0 +1,573 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors + +package echo + +import ( + "encoding" + "encoding/json" + "fmt" + "strconv" + "time" +) + +// TimeLayout specifies the format for parsing time values in request parameters. +// It can be a standard Go time layout string or one of the special Unix time layouts. +type TimeLayout string + +// TimeOpts is options for parsing time.Time values +type TimeOpts struct { + // Layout specifies the format for parsing time values in request parameters. + // It can be a standard Go time layout string or one of the special Unix time layouts. + // + // Parsing layout defaults to: echo.TimeLayout(time.RFC3339Nano) + // - To convert to custom layout use `echo.TimeLayout("2006-01-02")` + // - To convert unix timestamp (integer) to time.Time use `echo.TimeLayoutUnixTime` + // - To convert unix timestamp in milliseconds to time.Time use `echo.TimeLayoutUnixTimeMilli` + // - To convert unix timestamp in nanoseconds to time.Time use `echo.TimeLayoutUnixTimeNano` + Layout TimeLayout + + // ParseInLocation is location used with time.ParseInLocation for layout that do not contain + // timezone information to set output time in given location. + // Defaults to time.UTC + ParseInLocation *time.Location + + // ToInLocation is location to which parsed time is converted to after parsing. + // The parsed time will be converted using time.In(ToInLocation). + // Defaults to time.UTC + ToInLocation *time.Location +} + +// TimeLayout constants for parsing Unix timestamps in different precisions. +const ( + TimeLayoutUnixTime = TimeLayout("UnixTime") // Unix timestamp in seconds + TimeLayoutUnixTimeMilli = TimeLayout("UnixTimeMilli") // Unix timestamp in milliseconds + TimeLayoutUnixTimeNano = TimeLayout("UnixTimeNano") // Unix timestamp in nanoseconds +) + +// PathParam extracts and parses a path parameter from the context by name. +// It returns the typed value and an error if binding fails. Returns ErrNonExistentKey if parameter not found. +// +// Empty String Handling: +// +// If the parameter exists but has an empty value, the zero value of type T is returned +// with no error. For example, a path parameter with value "" returns (0, nil) for int types. +// This differs from standard library behavior where parsing empty strings returns errors. +// To treat empty values as errors, validate the result separately or check the raw value. +// +// See ParseValue for supported types and options +func PathParam[T any](c Context, paramName string, opts ...any) (T, error) { + for i, name := range c.ParamNames() { + if name == paramName { + pValues := c.ParamValues() + v, err := ParseValue[T](pValues[i], opts...) + if err != nil { + return v, NewBindingError(paramName, []string{pValues[i]}, "path param", err) + } + return v, nil + } + } + var zero T + return zero, ErrNonExistentKey +} + +// PathParamOr extracts and parses a path parameter from the context by name. +// Returns defaultValue if the parameter is not found or has an empty value. +// Returns an error only if parsing fails (e.g., "abc" for int type). +// +// Example: +// +// id, err := echo.PathParamOr[int](c, "id", 0) +// // If "id" is missing: returns (0, nil) +// // If "id" is "123": returns (123, nil) +// // If "id" is "abc": returns (0, BindingError) +// +// See ParseValue for supported types and options +func PathParamOr[T any](c Context, paramName string, defaultValue T, opts ...any) (T, error) { + for i, name := range c.ParamNames() { + if name == paramName { + pValues := c.ParamValues() + v, err := ParseValueOr[T](pValues[i], defaultValue, opts...) + if err != nil { + return v, NewBindingError(paramName, []string{pValues[i]}, "path param", err) + } + return v, nil + } + } + return defaultValue, nil +} + +// QueryParam extracts and parses a single query parameter from the request by key. +// It returns the typed value and an error if binding fails. Returns ErrNonExistentKey if parameter not found. +// +// Empty String Handling: +// +// If the parameter exists but has an empty value (?key=), the zero value of type T is returned +// with no error. For example, "?count=" returns (0, nil) for int types. +// This differs from standard library behavior where parsing empty strings returns errors. +// To treat empty values as errors, validate the result separately or check the raw value. +// +// Behavior Summary: +// - Missing key (?other=value): returns (zero, ErrNonExistentKey) +// - Empty value (?key=): returns (zero, nil) +// - Invalid value (?key=abc for int): returns (zero, BindingError) +// +// See ParseValue for supported types and options +func QueryParam[T any](c Context, key string, opts ...any) (T, error) { + values, ok := c.QueryParams()[key] + if !ok { + var zero T + return zero, ErrNonExistentKey + } + if len(values) == 0 { + var zero T + return zero, nil + } + value := values[0] + v, err := ParseValue[T](value, opts...) + if err != nil { + return v, NewBindingError(key, []string{value}, "query param", err) + } + return v, nil +} + +// QueryParamOr extracts and parses a single query parameter from the request by key. +// Returns defaultValue if the parameter is not found or has an empty value. +// Returns an error only if parsing fails (e.g., "abc" for int type). +// +// Example: +// +// page, err := echo.QueryParamOr[int](c, "page", 1) +// // If "page" is missing: returns (1, nil) +// // If "page" is "5": returns (5, nil) +// // If "page" is "abc": returns (1, BindingError) +// +// See ParseValue for supported types and options +func QueryParamOr[T any](c Context, key string, defaultValue T, opts ...any) (T, error) { + values, ok := c.QueryParams()[key] + if !ok { + return defaultValue, nil + } + if len(values) == 0 { + return defaultValue, nil + } + value := values[0] + v, err := ParseValueOr[T](value, defaultValue, opts...) + if err != nil { + return v, NewBindingError(key, []string{value}, "query param", err) + } + return v, nil +} + +// QueryParams extracts and parses all values for a query parameter key as a slice. +// It returns the typed slice and an error if binding any value fails. Returns ErrNonExistentKey if parameter not found. +// +// See ParseValues for supported types and options +func QueryParams[T any](c Context, key string, opts ...any) ([]T, error) { + values, ok := c.QueryParams()[key] + if !ok { + return nil, ErrNonExistentKey + } + + result, err := ParseValues[T](values, opts...) + if err != nil { + return nil, NewBindingError(key, values, "query params", err) + } + return result, nil +} + +// QueryParamsOr extracts and parses all values for a query parameter key as a slice. +// Returns defaultValue if the parameter is not found. +// Returns an error only if parsing any value fails. +// +// Example: +// +// ids, err := echo.QueryParamsOr[int](c, "ids", []int{}) +// // If "ids" is missing: returns ([], nil) +// // If "ids" is "1&ids=2": returns ([1, 2], nil) +// // If "ids" contains "abc": returns ([], BindingError) +// +// See ParseValues for supported types and options +func QueryParamsOr[T any](c Context, key string, defaultValue []T, opts ...any) ([]T, error) { + values, ok := c.QueryParams()[key] + if !ok { + return defaultValue, nil + } + + result, err := ParseValuesOr[T](values, defaultValue, opts...) + if err != nil { + return nil, NewBindingError(key, values, "query params", err) + } + return result, nil +} + +// FormParam extracts and parses a single form value from the request by key. +// It returns the typed value and an error if binding fails. Returns ErrNonExistentKey if parameter not found. +// +// Empty String Handling: +// +// If the form field exists but has an empty value, the zero value of type T is returned +// with no error. For example, an empty form field returns (0, nil) for int types. +// This differs from standard library behavior where parsing empty strings returns errors. +// To treat empty values as errors, validate the result separately or check the raw value. +// +// See ParseValue for supported types and options +func FormParam[T any](c Context, key string, opts ...any) (T, error) { + formValues, err := c.FormParams() + if err != nil { + var zero T + return zero, fmt.Errorf("failed to parse form param, key: %s, err: %w", key, err) + } + values, ok := formValues[key] + if !ok { + var zero T + return zero, ErrNonExistentKey + } + if len(values) == 0 { + var zero T + return zero, nil + } + value := values[0] + v, err := ParseValue[T](value, opts...) + if err != nil { + return v, NewBindingError(key, []string{value}, "form param", err) + } + return v, nil +} + +// FormParamOr extracts and parses a single form value from the request by key. +// Returns defaultValue if the parameter is not found or has an empty value. +// Returns an error only if parsing fails or form parsing errors occur. +// +// Example: +// +// limit, err := echo.FormValueOr[int](c, "limit", 100) +// // If "limit" is missing: returns (100, nil) +// // If "limit" is "50": returns (50, nil) +// // If "limit" is "abc": returns (100, BindingError) +// +// See ParseValue for supported types and options +func FormParamOr[T any](c Context, key string, defaultValue T, opts ...any) (T, error) { + formValues, err := c.FormParams() + if err != nil { + var zero T + return zero, fmt.Errorf("failed to parse form param, key: %s, err: %w", key, err) + } + values, ok := formValues[key] + if !ok { + return defaultValue, nil + } + if len(values) == 0 { + return defaultValue, nil + } + value := values[0] + v, err := ParseValueOr[T](value, defaultValue, opts...) + if err != nil { + return v, NewBindingError(key, []string{value}, "form param", err) + } + return v, nil +} + +// FormParams extracts and parses all values for a form values key as a slice. +// It returns the typed slice and an error if binding any value fails. Returns ErrNonExistentKey if parameter not found. +// +// See ParseValues for supported types and options +func FormParams[T any](c Context, key string, opts ...any) ([]T, error) { + formValues, err := c.FormParams() + if err != nil { + return nil, fmt.Errorf("failed to parse form params, key: %s, err: %w", key, err) + } + values, ok := formValues[key] + if !ok { + return nil, ErrNonExistentKey + } + result, err := ParseValues[T](values, opts...) + if err != nil { + return nil, NewBindingError(key, values, "form params", err) + } + return result, nil +} + +// FormParamsOr extracts and parses all values for a form values key as a slice. +// Returns defaultValue if the parameter is not found. +// Returns an error only if parsing any value fails or form parsing errors occur. +// +// Example: +// +// tags, err := echo.FormParamsOr[string](c, "tags", []string{}) +// // If "tags" is missing: returns ([], nil) +// // If form parsing fails: returns (nil, error) +// +// See ParseValues for supported types and options +func FormParamsOr[T any](c Context, key string, defaultValue []T, opts ...any) ([]T, error) { + formValues, err := c.FormParams() + if err != nil { + return nil, fmt.Errorf("failed to parse form params, key: %s, err: %w", key, err) + } + values, ok := formValues[key] + if !ok { + return defaultValue, nil + } + result, err := ParseValuesOr[T](values, defaultValue, opts...) + if err != nil { + return nil, NewBindingError(key, values, "form params", err) + } + return result, nil +} + +// ParseValues parses value to generic type slice. Same types are supported as ParseValue +// function but the result type is slice instead of scalar value. +// +// See ParseValue for supported types and options +func ParseValues[T any](values []string, opts ...any) ([]T, error) { + var zero []T + return ParseValuesOr(values, zero, opts...) +} + +// ParseValuesOr parses value to generic type slice, when value is empty defaultValue is returned. +// Same types are supported as ParseValue function but the result type is slice instead of scalar value. +// +// See ParseValue for supported types and options +func ParseValuesOr[T any](values []string, defaultValue []T, opts ...any) ([]T, error) { + if len(values) == 0 { + return defaultValue, nil + } + result := make([]T, 0, len(values)) + for _, v := range values { + tmp, err := ParseValue[T](v, opts...) + if err != nil { + return nil, err + } + result = append(result, tmp) + } + return result, nil +} + +// ParseValue parses value to generic type +// +// Types that are supported: +// - bool +// - float32 +// - float64 +// - int +// - int8 +// - int16 +// - int32 +// - int64 +// - uint +// - uint8/byte +// - uint16 +// - uint32 +// - uint64 +// - string +// - echo.BindUnmarshaler interface +// - encoding.TextUnmarshaler interface +// - json.Unmarshaler interface +// - time.Duration +// - time.Time use echo.TimeOpts or echo.TimeLayout to set time parsing configuration +func ParseValue[T any](value string, opts ...any) (T, error) { + var zero T + return ParseValueOr(value, zero, opts...) +} + +// ParseValueOr parses value to generic type, when value is empty defaultValue is returned. +// +// Types that are supported: +// - bool +// - float32 +// - float64 +// - int +// - int8 +// - int16 +// - int32 +// - int64 +// - uint +// - uint8/byte +// - uint16 +// - uint32 +// - uint64 +// - string +// - echo.BindUnmarshaler interface +// - encoding.TextUnmarshaler interface +// - json.Unmarshaler interface +// - time.Duration +// - time.Time use echo.TimeOpts or echo.TimeLayout to set time parsing configuration +func ParseValueOr[T any](value string, defaultValue T, opts ...any) (T, error) { + if len(value) == 0 { + return defaultValue, nil + } + var tmp T + if err := bindValue(value, &tmp, opts...); err != nil { + var zero T + return zero, fmt.Errorf("failed to parse value, err: %w", err) + } + return tmp, nil +} + +func bindValue(value string, dest any, opts ...any) error { + // NOTE: if this function is ever made public the dest should be checked for nil + // values when dealing with interfaces + if len(opts) > 0 { + if _, isTime := dest.(*time.Time); !isTime { + return fmt.Errorf("options are only supported for time.Time, got %T", dest) + } + } + + switch d := dest.(type) { + case *bool: + n, err := strconv.ParseBool(value) + if err != nil { + return err + } + *d = n + case *float32: + n, err := strconv.ParseFloat(value, 32) + if err != nil { + return err + } + *d = float32(n) + case *float64: + n, err := strconv.ParseFloat(value, 64) + if err != nil { + return err + } + *d = n + case *int: + n, err := strconv.ParseInt(value, 10, 0) + if err != nil { + return err + } + *d = int(n) + case *int8: + n, err := strconv.ParseInt(value, 10, 8) + if err != nil { + return err + } + *d = int8(n) + case *int16: + n, err := strconv.ParseInt(value, 10, 16) + if err != nil { + return err + } + *d = int16(n) + case *int32: + n, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + *d = int32(n) + case *int64: + n, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + *d = n + case *uint: + n, err := strconv.ParseUint(value, 10, 0) + if err != nil { + return err + } + *d = uint(n) + case *uint8: + n, err := strconv.ParseUint(value, 10, 8) + if err != nil { + return err + } + *d = uint8(n) + case *uint16: + n, err := strconv.ParseUint(value, 10, 16) + if err != nil { + return err + } + *d = uint16(n) + case *uint32: + n, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return err + } + *d = uint32(n) + case *uint64: + n, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + *d = n + case *string: + *d = value + case *time.Duration: + t, err := time.ParseDuration(value) + if err != nil { + return err + } + *d = t + case *time.Time: + to := TimeOpts{ + Layout: TimeLayout(time.RFC3339Nano), + ParseInLocation: time.UTC, + ToInLocation: time.UTC, + } + for _, o := range opts { + switch v := o.(type) { + case TimeOpts: + if v.Layout != "" { + to.Layout = v.Layout + } + if v.ParseInLocation != nil { + to.ParseInLocation = v.ParseInLocation + } + if v.ToInLocation != nil { + to.ToInLocation = v.ToInLocation + } + case TimeLayout: + to.Layout = v + } + } + var t time.Time + var err error + switch to.Layout { + case TimeLayoutUnixTime: + n, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + t = time.Unix(n, 0) + case TimeLayoutUnixTimeMilli: + n, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + t = time.UnixMilli(n) + case TimeLayoutUnixTimeNano: + n, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + t = time.Unix(0, n) + default: + if to.ParseInLocation != nil { + t, err = time.ParseInLocation(string(to.Layout), value, to.ParseInLocation) + } else { + t, err = time.Parse(string(to.Layout), value) + } + if err != nil { + return err + } + } + *d = t.In(to.ToInLocation) + case BindUnmarshaler: + if err := d.UnmarshalParam(value); err != nil { + return err + } + case encoding.TextUnmarshaler: + if err := d.UnmarshalText([]byte(value)); err != nil { + return err + } + case json.Unmarshaler: + if err := d.UnmarshalJSON([]byte(value)); err != nil { + return err + } + default: + return fmt.Errorf("unsupported value type: %T", dest) + } + return nil +} diff --git a/vendor/github.com/labstack/echo/v4/context.go b/vendor/github.com/labstack/echo/v4/context.go index 4edaa2ee1..6500e5eef 100644 --- a/vendor/github.com/labstack/echo/v4/context.go +++ b/vendor/github.com/labstack/echo/v4/context.go @@ -97,22 +97,22 @@ type Context interface { Cookies() []*http.Cookie // Get retrieves data from the context. - Get(key string) interface{} + Get(key string) any // Set saves data in the context. - Set(key string, val interface{}) + Set(key string, val any) // Bind binds path params, query params and the request body into provided type `i`. The default binder // binds body based on Content-Type header. - Bind(i interface{}) error + Bind(i any) error // Validate validates provided `i`. It is usually called after `Context#Bind()`. // Validator must be registered using `Echo#Validator`. - Validate(i interface{}) error + Validate(i any) error // Render renders a template with data and sends a text/html response with status // code. Renderer must be registered using `Echo.Renderer`. - Render(code int, name string, data interface{}) error + Render(code int, name string, data any) error // HTML sends an HTTP response with status code. HTML(code int, html string) error @@ -124,27 +124,27 @@ type Context interface { String(code int, s string) error // JSON sends a JSON response with status code. - JSON(code int, i interface{}) error + JSON(code int, i any) error // JSONPretty sends a pretty-print JSON with status code. - JSONPretty(code int, i interface{}, indent string) error + JSONPretty(code int, i any, indent string) error // JSONBlob sends a JSON blob response with status code. JSONBlob(code int, b []byte) error // JSONP sends a JSONP response with status code. It uses `callback` to construct // the JSONP payload. - JSONP(code int, callback string, i interface{}) error + JSONP(code int, callback string, i any) error // JSONPBlob sends a JSONP blob response with status code. It uses `callback` // to construct the JSONP payload. JSONPBlob(code int, callback string, b []byte) error // XML sends an XML response with status code. - XML(code int, i interface{}) error + XML(code int, i any) error // XMLPretty sends a pretty-print XML with status code. - XMLPretty(code int, i interface{}, indent string) error + XMLPretty(code int, i any, indent string) error // XMLBlob sends an XML blob response with status code. XMLBlob(code int, b []byte) error @@ -200,31 +200,31 @@ type Context interface { } type context struct { + logger Logger request *http.Request response *Response query url.Values echo *Echo - logger Logger store Map lock sync.RWMutex // following fields are set by Router + handler HandlerFunc // path is route path that Router matched. It is empty string where there is no route match. // Route registered with RouteNotFound is considered as a match and path therefore is not empty. path string - // pnames length is tied to param count for the matched route - pnames []string - // Usually echo.Echo is sizing pvalues but there could be user created middlewares that decide to // overwrite parameter by calling SetParamNames + SetParamValues. // When echo.Echo allocated that slice it length/capacity is tied to echo.Echo.maxParam value. // // It is important that pvalues size is always equal or bigger to pnames length. pvalues []string - handler HandlerFunc + + // pnames length is tied to param count for the matched route + pnames []string } const ( @@ -272,22 +272,35 @@ func (c *context) IsWebSocket() bool { return strings.EqualFold(upgrade, "websocket") } +func isValidProto(proto string) bool { + if proto == "" { + return false + } + for _, p := range []string{"http", "https", "ws", "wss"} { + if strings.EqualFold(proto, p) { + return true + } + } + return false +} + +// Scheme returns the HTTP protocol scheme, `http` or `https`. func (c *context) Scheme() string { // Can't use `r.Request.URL.Scheme` // See: https://groups.google.com/forum/#!topic/golang-nuts/pMUkBlQBDF0 if c.IsTLS() { return "https" } - if scheme := c.request.Header.Get(HeaderXForwardedProto); scheme != "" { + if scheme := c.request.Header.Get(HeaderXForwardedProto); isValidProto(scheme) { return scheme } - if scheme := c.request.Header.Get(HeaderXForwardedProtocol); scheme != "" { + if scheme := c.request.Header.Get(HeaderXForwardedProtocol); isValidProto(scheme) { return scheme } if ssl := c.request.Header.Get(HeaderXForwardedSsl); ssl == "on" { return "https" } - if scheme := c.request.Header.Get(HeaderXUrlScheme); scheme != "" { + if scheme := c.request.Header.Get(HeaderXUrlScheme); isValidProto(scheme) { return scheme } return "http" @@ -359,7 +372,7 @@ func (c *context) ParamValues() []string { func (c *context) SetParamValues(values ...string) { // NOTE: Don't just set c.pvalues = values, because it has to have length c.echo.maxParam (or bigger) at all times - // It will brake the Router#Find code + // It will break the Router#Find code limit := len(values) if limit > len(c.pvalues) { c.pvalues = make([]string, limit) @@ -430,13 +443,13 @@ func (c *context) Cookies() []*http.Cookie { return c.request.Cookies() } -func (c *context) Get(key string) interface{} { +func (c *context) Get(key string) any { c.lock.RLock() defer c.lock.RUnlock() return c.store[key] } -func (c *context) Set(key string, val interface{}) { +func (c *context) Set(key string, val any) { c.lock.Lock() defer c.lock.Unlock() @@ -446,18 +459,18 @@ func (c *context) Set(key string, val interface{}) { c.store[key] = val } -func (c *context) Bind(i interface{}) error { +func (c *context) Bind(i any) error { return c.echo.Binder.Bind(i, c) } -func (c *context) Validate(i interface{}) error { +func (c *context) Validate(i any) error { if c.echo.Validator == nil { return ErrValidatorNotRegistered } return c.echo.Validator.Validate(i) } -func (c *context) Render(code int, name string, data interface{}) (err error) { +func (c *context) Render(code int, name string, data any) (err error) { if c.echo.Renderer == nil { return ErrRendererNotRegistered } @@ -480,7 +493,7 @@ func (c *context) String(code int, s string) (err error) { return c.Blob(code, MIMETextPlainCharsetUTF8, []byte(s)) } -func (c *context) jsonPBlob(code int, callback string, i interface{}) (err error) { +func (c *context) jsonPBlob(code int, callback string, i any) (err error) { indent := "" if _, pretty := c.QueryParams()["pretty"]; c.echo.Debug || pretty { indent = defaultIndent @@ -499,13 +512,13 @@ func (c *context) jsonPBlob(code int, callback string, i interface{}) (err error return } -func (c *context) json(code int, i interface{}, indent string) error { +func (c *context) json(code int, i any, indent string) error { c.writeContentType(MIMEApplicationJSON) c.response.Status = code return c.echo.JSONSerializer.Serialize(c, i, indent) } -func (c *context) JSON(code int, i interface{}) (err error) { +func (c *context) JSON(code int, i any) (err error) { indent := "" if _, pretty := c.QueryParams()["pretty"]; c.echo.Debug || pretty { indent = defaultIndent @@ -513,7 +526,7 @@ func (c *context) JSON(code int, i interface{}) (err error) { return c.json(code, i, indent) } -func (c *context) JSONPretty(code int, i interface{}, indent string) (err error) { +func (c *context) JSONPretty(code int, i any, indent string) (err error) { return c.json(code, i, indent) } @@ -521,7 +534,7 @@ func (c *context) JSONBlob(code int, b []byte) (err error) { return c.Blob(code, MIMEApplicationJSON, b) } -func (c *context) JSONP(code int, callback string, i interface{}) (err error) { +func (c *context) JSONP(code int, callback string, i any) (err error) { return c.jsonPBlob(code, callback, i) } @@ -538,7 +551,7 @@ func (c *context) JSONPBlob(code int, callback string, b []byte) (err error) { return } -func (c *context) xml(code int, i interface{}, indent string) (err error) { +func (c *context) xml(code int, i any, indent string) (err error) { c.writeContentType(MIMEApplicationXMLCharsetUTF8) c.response.WriteHeader(code) enc := xml.NewEncoder(c.response) @@ -551,7 +564,7 @@ func (c *context) xml(code int, i interface{}, indent string) (err error) { return enc.Encode(i) } -func (c *context) XML(code int, i interface{}) (err error) { +func (c *context) XML(code int, i any) (err error) { indent := "" if _, pretty := c.QueryParams()["pretty"]; c.echo.Debug || pretty { indent = defaultIndent @@ -559,7 +572,7 @@ func (c *context) XML(code int, i interface{}) (err error) { return c.xml(code, i, indent) } -func (c *context) XMLPretty(code int, i interface{}, indent string) (err error) { +func (c *context) XMLPretty(code int, i any, indent string) (err error) { return c.xml(code, i, indent) } diff --git a/vendor/github.com/labstack/echo/v4/context_generic.go b/vendor/github.com/labstack/echo/v4/context_generic.go new file mode 100644 index 000000000..f06041bbf --- /dev/null +++ b/vendor/github.com/labstack/echo/v4/context_generic.go @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors + +package echo + +import "errors" + +// ErrNonExistentKey is error that is returned when key does not exist +var ErrNonExistentKey = errors.New("non existent key") + +// ErrInvalidKeyType is error that is returned when the value is not castable to expected type. +var ErrInvalidKeyType = errors.New("invalid key type") + +// ContextGet retrieves a value from the context store or ErrNonExistentKey error the key is missing. +// Returns ErrInvalidKeyType error if the value is not castable to type T. +func ContextGet[T any](c Context, key string) (T, error) { + val := c.Get(key) + if val == any(nil) { + var zero T + return zero, ErrNonExistentKey + } + + typed, ok := val.(T) + if !ok { + var zero T + return zero, ErrInvalidKeyType + } + + return typed, nil +} + +// ContextGetOr retrieves a value from the context store or returns a default value when the key +// is missing. Returns ErrInvalidKeyType error if the value is not castable to type T. +func ContextGetOr[T any](c Context, key string, defaultValue T) (T, error) { + typed, err := ContextGet[T](c, key) + if err == ErrNonExistentKey { + return defaultValue, nil + } + return typed, err +} diff --git a/vendor/github.com/labstack/echo/v4/echo.go b/vendor/github.com/labstack/echo/v4/echo.go index ab66b0da8..f00fb10e6 100644 --- a/vendor/github.com/labstack/echo/v4/echo.go +++ b/vendor/github.com/labstack/echo/v4/echo.go @@ -45,7 +45,6 @@ import ( "encoding/json" "errors" "fmt" - "io" stdLog "log" "net" "net/http" @@ -60,6 +59,7 @@ import ( "golang.org/x/crypto/acme" "golang.org/x/crypto/acme/autocert" "golang.org/x/net/http2" + //lint:ignore SA1019 h2c is required until v4 is supported (end of 2026) "golang.org/x/net/http2/h2c" ) @@ -91,10 +91,6 @@ type Echo struct { Listener net.Listener TLSListener net.Listener AutoTLSManager autocert.Manager - DisableHTTP2 bool - Debug bool - HideBanner bool - HidePort bool HTTPErrorHandler HTTPErrorHandler Binder Binder JSONSerializer JSONSerializer @@ -106,6 +102,21 @@ type Echo struct { // OnAddRouteHandler is called when Echo adds new route to specific host router. OnAddRouteHandler func(host string, route Route, handler HandlerFunc, middleware []MiddlewareFunc) + DisableHTTP2 bool + Debug bool + HideBanner bool + HidePort bool + + // EnablePathUnescapingStaticFiles enables path parameter (param: *) unescaping for Static/StaticFS methods. + // Default false (safe): encoded slashes (%2f) in the wildcard param are NOT decoded, + // preventing ACL bypass where /admin%2fprivate.txt bypasses a /admin/* route guard by + // not matching that route but having its wildcard param decoded to admin/private.txt. + // Set to true only when serving files whose names contain URL-encoded characters + // (e.g. "hello world.txt" via /hello%20world.txt) and you are not relying on + // route-based ACL guards to restrict access. + // If you are enabling this option, make sure you understand the security implications. + // See: https://github.com/labstack/echo/security/advisories/GHSA-vfp3-v2gw-7wfq + EnablePathUnescapingStaticFiles bool } // Route contains a handler and information for matching against requests. @@ -117,9 +128,9 @@ type Route struct { // HTTPError represents an error that occurred while handling a request. type HTTPError struct { - Code int `json:"-"` - Message interface{} `json:"message"` Internal error `json:"-"` // Stores the error returned by an external dependency + Message interface{} `json:"message"` + Code int `json:"-"` } // MiddlewareFunc defines a function to process middleware. @@ -142,11 +153,6 @@ type JSONSerializer interface { Deserialize(c Context, i interface{}) error } -// Renderer is the interface that wraps the Render function. -type Renderer interface { - Render(io.Writer, string, interface{}, Context) error -} - // Map defines a generic map of type `map[string]interface{}`. type Map map[string]interface{} @@ -238,9 +244,12 @@ const ( HeaderXCorrelationID = "X-Correlation-Id" HeaderXRequestedWith = "X-Requested-With" HeaderServer = "Server" - HeaderOrigin = "Origin" - HeaderCacheControl = "Cache-Control" - HeaderConnection = "Connection" + + // HeaderOrigin request header indicates the origin (scheme, hostname, and port) that caused the request. + // See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin + HeaderOrigin = "Origin" + HeaderCacheControl = "Cache-Control" + HeaderConnection = "Connection" // Access control HeaderAccessControlRequestMethod = "Access-Control-Request-Method" @@ -261,11 +270,16 @@ const ( HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only" HeaderXCSRFToken = "X-CSRF-Token" HeaderReferrerPolicy = "Referrer-Policy" + + // HeaderSecFetchSite fetch metadata request header indicates the relationship between a request initiator's + // origin and the origin of the requested resource. + // See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site + HeaderSecFetchSite = "Sec-Fetch-Site" ) const ( // Version of Echo - Version = "4.12.0" + Version = "4.15.4" website = "https://echo.labstack.com" // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo banner = ` @@ -843,6 +857,7 @@ func (e *Echo) StartH2CServer(address string, h2s *http2.Server) error { s.Addr = address e.colorer.SetOutput(e.Logger.Output()) s.ErrorLog = e.StdLogger + //lint:ignore SA1019 h2c is required until v4 is supported (end of 2026) s.Handler = h2c.NewHandler(e, h2s) if e.Debug { e.Logger.SetLevel(log.DEBUG) diff --git a/vendor/github.com/labstack/echo/v4/echo_fs.go b/vendor/github.com/labstack/echo/v4/echo_fs.go index a7b231f31..ed27b992e 100644 --- a/vendor/github.com/labstack/echo/v4/echo_fs.go +++ b/vendor/github.com/labstack/echo/v4/echo_fs.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" "os" + "path" "path/filepath" "strings" ) @@ -35,7 +36,7 @@ func (e *Echo) Static(pathPrefix, fsRoot string) *Route { return e.Add( http.MethodGet, pathPrefix+"*", - StaticDirectoryHandler(subFs, false), + StaticDirectoryHandler(subFs, !e.EnablePathUnescapingStaticFiles), ) } @@ -48,12 +49,19 @@ func (e *Echo) StaticFS(pathPrefix string, filesystem fs.FS) *Route { return e.Add( http.MethodGet, pathPrefix+"*", - StaticDirectoryHandler(filesystem, false), + StaticDirectoryHandler(filesystem, !e.EnablePathUnescapingStaticFiles), ) } -// StaticDirectoryHandler creates handler function to serve files from provided file system +// StaticDirectoryHandler creates handler function to serve files from provided file system. // When disablePathUnescaping is set then file name from path is not unescaped and is served as is. +// +// Note: when disablePathUnescaping=false, the handler decodes the wildcard param before serving. +// If route guards (e.g. e.GET("/admin/*", forbidden)) are used to restrict parts of the +// filesystem, an encoded separator (%2F) or encoded dot-dot (%2E%2E) in the URL can resolve to +// a path that the router never matched against the guard route. Do not rely on route guards +// alone to restrict a filesystem served by this handler. +// See https://github.com/labstack/echo/security/advisories/GHSA-vfp3-v2gw-7wfq func StaticDirectoryHandler(fileSystem fs.FS, disablePathUnescaping bool) HandlerFunc { return func(c Context) error { p := c.Param("*") @@ -65,8 +73,11 @@ func StaticDirectoryHandler(fileSystem fs.FS, disablePathUnescaping bool) Handle p = tmpPath } - // fs.FS.Open() already assumes that file names are relative to FS root path and considers name with prefix `/` as invalid - name := filepath.ToSlash(filepath.Clean(strings.TrimPrefix(p, "/"))) + // fs.FS.Open() already assumes that file names are relative to FS root path and considers name with prefix `/` as invalid. + // Use path.Clean (not filepath.Clean): fs.FS paths are always forward-slash, so a backslash must stay a literal + // character rather than being interpreted as a separator on Windows (which would resolve a file across a boundary + // the router never matched on). + name := path.Clean(strings.TrimPrefix(p, "/")) fi, err := fs.Stat(fileSystem, name) if err != nil { return ErrNotFound @@ -102,8 +113,8 @@ func StaticFileHandler(file string, filesystem fs.FS) HandlerFunc { // traverse up from current executable run path. // NB: private because you really should use fs.FS implementation instances type defaultFS struct { - prefix string fs fs.FS + prefix string } func newDefaultFS() *defaultFS { diff --git a/vendor/github.com/labstack/echo/v4/group.go b/vendor/github.com/labstack/echo/v4/group.go index eca25c947..cb37b123f 100644 --- a/vendor/github.com/labstack/echo/v4/group.go +++ b/vendor/github.com/labstack/echo/v4/group.go @@ -14,8 +14,8 @@ type Group struct { common host string prefix string - middleware []MiddlewareFunc echo *Echo + middleware []MiddlewareFunc } // Use implements `Echo#Use()` for sub-routes within the Group. diff --git a/vendor/github.com/labstack/echo/v4/group_fs.go b/vendor/github.com/labstack/echo/v4/group_fs.go index c1b7ec2d3..5246ea06b 100644 --- a/vendor/github.com/labstack/echo/v4/group_fs.go +++ b/vendor/github.com/labstack/echo/v4/group_fs.go @@ -23,7 +23,7 @@ func (g *Group) StaticFS(pathPrefix string, filesystem fs.FS) { g.Add( http.MethodGet, pathPrefix+"*", - StaticDirectoryHandler(filesystem, false), + StaticDirectoryHandler(filesystem, !g.echo.EnablePathUnescapingStaticFiles), ) } diff --git a/vendor/github.com/labstack/echo/v4/ip.go b/vendor/github.com/labstack/echo/v4/ip.go index 6aed8d606..dce51f55d 100644 --- a/vendor/github.com/labstack/echo/v4/ip.go +++ b/vendor/github.com/labstack/echo/v4/ip.go @@ -24,7 +24,7 @@ To retrieve IP address reliably/securely, you must let your application be aware In Echo, this can be done by configuring `Echo#IPExtractor` appropriately. This guides show you why and how. -> Note: if you dont' set `Echo#IPExtractor` explicitly, Echo fallback to legacy behavior, which is not a good choice. +> Note: if you don't set `Echo#IPExtractor` explicitly, Echo fallback to legacy behavior, which is not a good choice. Let's start from two questions to know the right direction: @@ -134,10 +134,10 @@ Private IPv6 address ranges: */ type ipChecker struct { + trustExtraRanges []*net.IPNet trustLoopback bool trustLinkLocal bool trustPrivateNet bool - trustExtraRanges []*net.IPNet } // TrustOption is config for which IP address to trust @@ -179,16 +179,6 @@ func newIPChecker(configs []TrustOption) *ipChecker { return checker } -// Go1.16+ added `ip.IsPrivate()` but until that use this implementation -func isPrivateIPRange(ip net.IP) bool { - if ip4 := ip.To4(); ip4 != nil { - return ip4[0] == 10 || - ip4[0] == 172 && ip4[1]&0xf0 == 16 || - ip4[0] == 192 && ip4[1] == 168 - } - return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc -} - func (c *ipChecker) trust(ip net.IP) bool { if c.trustLoopback && ip.IsLoopback() { return true @@ -196,7 +186,7 @@ func (c *ipChecker) trust(ip net.IP) bool { if c.trustLinkLocal && ip.IsLinkLocalUnicast() { return true } - if c.trustPrivateNet && isPrivateIPRange(ip) { + if c.trustPrivateNet && ip.IsPrivate() { return true } for _, trustedRange := range c.trustExtraRanges { @@ -219,8 +209,14 @@ func ExtractIPDirect() IPExtractor { } func extractIP(req *http.Request) string { - ra, _, _ := net.SplitHostPort(req.RemoteAddr) - return ra + host, _, err := net.SplitHostPort(req.RemoteAddr) + if err != nil { + if net.ParseIP(req.RemoteAddr) != nil { + return req.RemoteAddr + } + return "" + } + return host } // ExtractIPFromRealIPHeader extracts IP address using x-real-ip header. diff --git a/vendor/github.com/labstack/echo/v4/middleware/basic_auth.go b/vendor/github.com/labstack/echo/v4/middleware/basic_auth.go index 9285f29fd..4a46098e3 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/basic_auth.go +++ b/vendor/github.com/labstack/echo/v4/middleware/basic_auth.go @@ -66,6 +66,9 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc { config.Realm = defaultRealm } + // Pre-compute the quoted realm for WWW-Authenticate header (RFC 7617) + quotedRealm := strconv.Quote(config.Realm) + return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { if config.Skipper(c) { @@ -84,27 +87,21 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc { } cred := string(b) - for i := 0; i < len(cred); i++ { - if cred[i] == ':' { - // Verify credentials - valid, err := config.Validator(cred[:i], cred[i+1:], c) - if err != nil { - return err - } else if valid { - return next(c) - } - break + user, pass, ok := strings.Cut(cred, ":") + if ok { + // Verify credentials + valid, err := config.Validator(user, pass, c) + if err != nil { + return err + } else if valid { + return next(c) } } } - realm := defaultRealm - if config.Realm != defaultRealm { - realm = strconv.Quote(config.Realm) - } - // Need to return `401` for browsers to pop-up login box. - c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm="+realm) + // Realm is case-insensitive, so we can use "basic" directly. See RFC 7617. + c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm="+quotedRealm) return echo.ErrUnauthorized } } diff --git a/vendor/github.com/labstack/echo/v4/middleware/body_dump.go b/vendor/github.com/labstack/echo/v4/middleware/body_dump.go index b06f76202..add778d67 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/body_dump.go +++ b/vendor/github.com/labstack/echo/v4/middleware/body_dump.go @@ -66,8 +66,12 @@ func BodyDumpWithConfig(config BodyDumpConfig) echo.MiddlewareFunc { // Request reqBody := []byte{} - if c.Request().Body != nil { // Read - reqBody, _ = io.ReadAll(c.Request().Body) + if c.Request().Body != nil { + var readErr error + reqBody, readErr = io.ReadAll(c.Request().Body) + if readErr != nil { + return readErr + } } c.Request().Body = io.NopCloser(bytes.NewBuffer(reqBody)) // Reset @@ -98,14 +102,14 @@ func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) { } func (w *bodyDumpResponseWriter) Flush() { - err := responseControllerFlush(w.ResponseWriter) + err := http.NewResponseController(w.ResponseWriter).Flush() if err != nil && errors.Is(err, http.ErrNotSupported) { panic(errors.New("response writer flushing is not supported")) } } func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return responseControllerHijack(w.ResponseWriter) + return http.NewResponseController(w.ResponseWriter).Hijack() } func (w *bodyDumpResponseWriter) Unwrap() http.ResponseWriter { diff --git a/vendor/github.com/labstack/echo/v4/middleware/body_limit.go b/vendor/github.com/labstack/echo/v4/middleware/body_limit.go index 7d3c665f2..d13ad2c4e 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/body_limit.go +++ b/vendor/github.com/labstack/echo/v4/middleware/body_limit.go @@ -6,6 +6,7 @@ package middleware import ( "fmt" "io" + "net/http" "sync" "github.com/labstack/echo/v4" @@ -77,7 +78,10 @@ func BodyLimitWithConfig(config BodyLimitConfig) echo.MiddlewareFunc { } // Based on content read - r := pool.Get().(*limitedReader) + r, ok := pool.Get().(*limitedReader) + if !ok { + return echo.NewHTTPError(http.StatusInternalServerError, "invalid pool object") + } r.Reset(req.Body) defer pool.Put(r) req.Body = r diff --git a/vendor/github.com/labstack/echo/v4/middleware/compress.go b/vendor/github.com/labstack/echo/v4/middleware/compress.go index 557bdc8e2..48ccc9856 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/compress.go +++ b/vendor/github.com/labstack/echo/v4/middleware/compress.go @@ -96,7 +96,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc { i := pool.Get() w, ok := i.(*gzip.Writer) if !ok { - return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error()) + return echo.NewHTTPError(http.StatusInternalServerError, "invalid pool object") } rw := res.Writer w.Reset(rw) @@ -189,8 +189,10 @@ func (w *gzipResponseWriter) Flush() { w.Writer.Write(w.buffer.Bytes()) } - w.Writer.(*gzip.Writer).Flush() - _ = responseControllerFlush(w.ResponseWriter) + if gw, ok := w.Writer.(*gzip.Writer); ok { + gw.Flush() + } + _ = http.NewResponseController(w.ResponseWriter).Flush() } func (w *gzipResponseWriter) Unwrap() http.ResponseWriter { @@ -198,7 +200,7 @@ func (w *gzipResponseWriter) Unwrap() http.ResponseWriter { } func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return responseControllerHijack(w.ResponseWriter) + return http.NewResponseController(w.ResponseWriter).Hijack() } func (w *gzipResponseWriter) Push(target string, opts *http.PushOptions) error { diff --git a/vendor/github.com/labstack/echo/v4/middleware/context_timeout.go b/vendor/github.com/labstack/echo/v4/middleware/context_timeout.go index e67173f21..5d9ae9755 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/context_timeout.go +++ b/vendor/github.com/labstack/echo/v4/middleware/context_timeout.go @@ -11,12 +11,45 @@ import ( "github.com/labstack/echo/v4" ) +// ContextTimeout Middleware +// +// ContextTimeout provides request timeout functionality using Go's context mechanism. +// It is the recommended replacement for the deprecated Timeout middleware. +// +// +// Basic Usage: +// +// e.Use(middleware.ContextTimeout(30 * time.Second)) +// +// With Configuration: +// +// e.Use(middleware.ContextTimeoutWithConfig(middleware.ContextTimeoutConfig{ +// Timeout: 30 * time.Second, +// Skipper: middleware.DefaultSkipper, +// })) +// +// Handler Example: +// +// e.GET("/task", func(c echo.Context) error { +// ctx := c.Request().Context() +// +// result, err := performTaskWithContext(ctx) +// if err != nil { +// if errors.Is(err, context.DeadlineExceeded) { +// return echo.NewHTTPError(http.StatusServiceUnavailable, "timeout") +// } +// return err +// } +// +// return c.JSON(http.StatusOK, result) +// }) + // ContextTimeoutConfig defines the config for ContextTimeout middleware. type ContextTimeoutConfig struct { // Skipper defines a function to skip middleware. Skipper Skipper - // ErrorHandler is a function when error aries in middleware execution. + // ErrorHandler is a function when error arises in middleware execution. ErrorHandler func(err error, c echo.Context) error // Timeout configures a timeout for the middleware, defaults to 0 for no timeout diff --git a/vendor/github.com/labstack/echo/v4/middleware/cors.go b/vendor/github.com/labstack/echo/v4/middleware/cors.go index 7af6a76f3..a1f445321 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/cors.go +++ b/vendor/github.com/labstack/echo/v4/middleware/cors.go @@ -147,13 +147,25 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc { config.AllowMethods = DefaultCORSConfig.AllowMethods } - allowOriginPatterns := []string{} + allowOriginPatterns := make([]*regexp.Regexp, 0, len(config.AllowOrigins)) for _, origin := range config.AllowOrigins { + if origin == "*" { + continue // "*" is handled differently and does not need regexp + } pattern := regexp.QuoteMeta(origin) pattern = strings.ReplaceAll(pattern, "\\*", ".*") pattern = strings.ReplaceAll(pattern, "\\?", ".") pattern = "^" + pattern + "$" - allowOriginPatterns = append(allowOriginPatterns, pattern) + + re, err := regexp.Compile(pattern) + if err != nil { + // this is to preserve previous behaviour - invalid patterns were just ignored. + // If we would turn this to panic, users with invalid patterns + // would have applications crashing in production due unrecovered panic. + // TODO: this should be turned to error/panic in `v5` + continue + } + allowOriginPatterns = append(allowOriginPatterns, re) } allowMethods := strings.Join(config.AllowMethods, ",") @@ -239,7 +251,7 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc { } if checkPatterns { for _, re := range allowOriginPatterns { - if match, _ := regexp.MatchString(re, origin); match { + if match := re.MatchString(origin); match { allowOrigin = origin break } diff --git a/vendor/github.com/labstack/echo/v4/middleware/csrf.go b/vendor/github.com/labstack/echo/v4/middleware/csrf.go index 92f4019dc..1a35da63c 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/csrf.go +++ b/vendor/github.com/labstack/echo/v4/middleware/csrf.go @@ -6,16 +6,43 @@ package middleware import ( "crypto/subtle" "net/http" + "slices" + "strings" "time" "github.com/labstack/echo/v4" ) +// CSRFUsingSecFetchSite is a context key for CSRF middleware what is set when the client browser is using Sec-Fetch-Site +// header and the request is deemed safe. +// It is a dummy token value that can be used to render CSRF token for form by handlers. +// +// We know that the client is using a browser that supports Sec-Fetch-Site header, so when the form is submitted in +// the future with this dummy token value it is OK. Although the request is safe, the template rendered by the +// handler may need this value to render CSRF token for form. +const CSRFUsingSecFetchSite = "_echo_csrf_using_sec_fetch_site_" + // CSRFConfig defines the config for CSRF middleware. type CSRFConfig struct { // Skipper defines a function to skip middleware. Skipper Skipper + // TrustedOrigin permits any request with `Sec-Fetch-Site` header whose `Origin` header + // exactly matches the specified value. + // Values should be formated as Origin header "scheme://host[:port]". + // + // See [Origin]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin + // See [Sec-Fetch-Site]: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#fetch-metadata-headers + TrustedOrigins []string + + // AllowSecFetchSameSite allows custom behaviour for `Sec-Fetch-Site` requests that are about to + // fail with CRSF error, to be allowed or replaced with custom error. + // This function applies to `Sec-Fetch-Site` values: + // - `same-site` same registrable domain (subdomain and/or different port) + // - `cross-site` request originates from different site + // See [Sec-Fetch-Site]: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#fetch-metadata-headers + AllowSecFetchSiteFunc func(c echo.Context) (bool, error) + // TokenLength is the length of the generated token. TokenLength uint8 `yaml:"token_length"` // Optional. Default value 32. @@ -65,6 +92,8 @@ type CSRFConfig struct { // ErrorHandler defines a function which is executed for returning custom errors. ErrorHandler CSRFErrorHandler + + generator func(length uint8) string } // CSRFErrorHandler is a function which is executed for creating custom errors. @@ -94,7 +123,11 @@ func CSRF() echo.MiddlewareFunc { // CSRFWithConfig returns a CSRF middleware with config. // See `CSRF()`. func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc { - // Defaults + return toMiddlewareOrPanic(config) +} + +// ToMiddleware converts CSRFConfig to middleware or returns an error for invalid configuration +func (config CSRFConfig) ToMiddleware() (echo.MiddlewareFunc, error) { if config.Skipper == nil { config.Skipper = DefaultCSRFConfig.Skipper } @@ -117,10 +150,20 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc { if config.CookieSameSite == http.SameSiteNoneMode { config.CookieSecure = true } + if len(config.TrustedOrigins) > 0 { + if vErr := validateOrigins(config.TrustedOrigins, "trusted origin"); vErr != nil { + return nil, vErr + } + config.TrustedOrigins = append([]string(nil), config.TrustedOrigins...) + } + tokenGenerator := randomString + if config.generator != nil { + tokenGenerator = config.generator + } extractors, cErr := CreateExtractors(config.TokenLookup) if cErr != nil { - panic(cErr) + return nil, cErr } return func(next echo.HandlerFunc) echo.HandlerFunc { @@ -129,9 +172,20 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc { return next(c) } + // use the `Sec-Fetch-Site` header as part of a modern approach to CSRF protection + allow, err := config.checkSecFetchSiteRequest(c) + if err != nil { + return err + } + if allow { + return next(c) + } + + // Fallback to legacy token based CSRF protection + token := "" if k, err := c.Cookie(config.CookieName); err != nil { - token = randomString(config.TokenLength) + token = tokenGenerator(config.TokenLength) } else { token = k.Value // Reuse token } @@ -210,9 +264,60 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc { return next(c) } - } + }, nil } func validateCSRFToken(token, clientToken string) bool { return subtle.ConstantTimeCompare([]byte(token), []byte(clientToken)) == 1 } + +var safeMethods = []string{http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace} + +func (config CSRFConfig) checkSecFetchSiteRequest(c echo.Context) (bool, error) { + // https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#fetch-metadata-headers + // Sec-Fetch-Site values are: + // - `same-origin` exact origin match - allow always + // - `same-site` same registrable domain (subdomain and/or different port) - block, unless explicitly trusted + // - `cross-site` request originates from different site - block, unless explicitly trusted + // - `none` direct navigation (URL bar, bookmark) - allow always + secFetchSite := c.Request().Header.Get(echo.HeaderSecFetchSite) + if secFetchSite == "" { + return false, nil + } + + if len(config.TrustedOrigins) > 0 { + // trusted sites ala OAuth callbacks etc. should be let through + origin := c.Request().Header.Get(echo.HeaderOrigin) + if origin != "" { + for _, trustedOrigin := range config.TrustedOrigins { + if strings.EqualFold(origin, trustedOrigin) { + return true, nil + } + } + } + } + isSafe := slices.Contains(safeMethods, c.Request().Method) + if !isSafe { // for state-changing request check SecFetchSite value + isSafe = secFetchSite == "same-origin" || secFetchSite == "none" + } + + if isSafe { + // This helps handlers that support older token-based CSRF protection. + // We know that the client is using a browser that supports Sec-Fetch-Site header, so when the form is submitted in + // the future with this dummy token value it is OK. Although the request is safe, the template rendered by the + // handler may need this value to render CSRF token for form. + c.Set(config.ContextKey, CSRFUsingSecFetchSite) + return true, nil + } + // we are here when request is state-changing and `cross-site` or `same-site` + + // Note: if you want to block `same-site` use config.TrustedOrigins or `config.AllowSecFetchSiteFunc` + if config.AllowSecFetchSiteFunc != nil { + return config.AllowSecFetchSiteFunc(c) + } + + if secFetchSite == "same-site" { + return false, nil // fall back to legacy token + } + return false, echo.NewHTTPError(http.StatusForbidden, "cross-site request blocked by CSRF") +} diff --git a/vendor/github.com/labstack/echo/v4/middleware/jwt.go b/vendor/github.com/labstack/echo/v4/middleware/jwt.go deleted file mode 100644 index a6bf16f95..000000000 --- a/vendor/github.com/labstack/echo/v4/middleware/jwt.go +++ /dev/null @@ -1,303 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors - -//go:build go1.15 -// +build go1.15 - -package middleware - -import ( - "errors" - "fmt" - "github.com/golang-jwt/jwt" - "github.com/labstack/echo/v4" - "net/http" - "reflect" -) - -// JWTConfig defines the config for JWT middleware. -type JWTConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // BeforeFunc defines a function which is executed just before the middleware. - BeforeFunc BeforeFunc - - // SuccessHandler defines a function which is executed for a valid token before middleware chain continues with next - // middleware or handler. - SuccessHandler JWTSuccessHandler - - // ErrorHandler defines a function which is executed for an invalid token. - // It may be used to define a custom JWT error. - ErrorHandler JWTErrorHandler - - // ErrorHandlerWithContext is almost identical to ErrorHandler, but it's passed the current context. - ErrorHandlerWithContext JWTErrorHandlerWithContext - - // ContinueOnIgnoredError allows the next middleware/handler to be called when ErrorHandlerWithContext decides to - // ignore the error (by returning `nil`). - // This is useful when parts of your site/api allow public access and some authorized routes provide extra functionality. - // In that case you can use ErrorHandlerWithContext to set a default public JWT token value in the request context - // and continue. Some logic down the remaining execution chain needs to check that (public) token value then. - ContinueOnIgnoredError bool - - // Signing key to validate token. - // This is one of the three options to provide a token validation key. - // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey. - // Required if neither user-defined KeyFunc nor SigningKeys is provided. - SigningKey interface{} - - // Map of signing keys to validate token with kid field usage. - // This is one of the three options to provide a token validation key. - // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey. - // Required if neither user-defined KeyFunc nor SigningKey is provided. - SigningKeys map[string]interface{} - - // Signing method used to check the token's signing algorithm. - // Optional. Default value HS256. - SigningMethod string - - // Context key to store user information from the token into context. - // Optional. Default value "user". - ContextKey string - - // Claims are extendable claims data defining token content. Used by default ParseTokenFunc implementation. - // Not used if custom ParseTokenFunc is set. - // Optional. Default value jwt.MapClaims - Claims jwt.Claims - - // TokenLookup is a string in the form of ":" or ":,:" that is used - // to extract token from the request. - // Optional. Default value "header:Authorization". - // Possible values: - // - "header:" or "header::" - // `` is argument value to cut/trim prefix of the extracted value. This is useful if header - // value has static prefix like `Authorization: ` where part that we - // want to cut is ` ` note the space at the end. - // In case of JWT tokens `Authorization: Bearer ` prefix we cut is `Bearer `. - // If prefix is left empty the whole value is returned. - // - "query:" - // - "param:" - // - "cookie:" - // - "form:" - // Multiple sources example: - // - "header:Authorization,cookie:myowncookie" - TokenLookup string - - // TokenLookupFuncs defines a list of user-defined functions that extract JWT token from the given context. - // This is one of the two options to provide a token extractor. - // The order of precedence is user-defined TokenLookupFuncs, and TokenLookup. - // You can also provide both if you want. - TokenLookupFuncs []ValuesExtractor - - // AuthScheme to be used in the Authorization header. - // Optional. Default value "Bearer". - AuthScheme string - - // KeyFunc defines a user-defined function that supplies the public key for a token validation. - // The function shall take care of verifying the signing algorithm and selecting the proper key. - // A user-defined KeyFunc can be useful if tokens are issued by an external party. - // Used by default ParseTokenFunc implementation. - // - // When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored. - // This is one of the three options to provide a token validation key. - // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey. - // Required if neither SigningKeys nor SigningKey is provided. - // Not used if custom ParseTokenFunc is set. - // Default to an internal implementation verifying the signing algorithm and selecting the proper key. - KeyFunc jwt.Keyfunc - - // ParseTokenFunc defines a user-defined function that parses token from given auth. Returns an error when token - // parsing fails or parsed token is invalid. - // Defaults to implementation using `github.com/golang-jwt/jwt` as JWT implementation library - ParseTokenFunc func(auth string, c echo.Context) (interface{}, error) -} - -// JWTSuccessHandler defines a function which is executed for a valid token. -type JWTSuccessHandler func(c echo.Context) - -// JWTErrorHandler defines a function which is executed for an invalid token. -type JWTErrorHandler func(err error) error - -// JWTErrorHandlerWithContext is almost identical to JWTErrorHandler, but it's passed the current context. -type JWTErrorHandlerWithContext func(err error, c echo.Context) error - -// Algorithms -const ( - AlgorithmHS256 = "HS256" -) - -// ErrJWTMissing is error that is returned when no JWToken was extracted from the request. -var ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt") - -// ErrJWTInvalid is error that is returned when middleware could not parse JWT correctly. -var ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "invalid or expired jwt") - -// DefaultJWTConfig is the default JWT auth middleware config. -var DefaultJWTConfig = JWTConfig{ - Skipper: DefaultSkipper, - SigningMethod: AlgorithmHS256, - ContextKey: "user", - TokenLookup: "header:" + echo.HeaderAuthorization, - TokenLookupFuncs: nil, - AuthScheme: "Bearer", - Claims: jwt.MapClaims{}, - KeyFunc: nil, -} - -// JWT returns a JSON Web Token (JWT) auth middleware. -// -// For valid token, it sets the user in context and calls next handler. -// For invalid token, it returns "401 - Unauthorized" error. -// For missing token, it returns "400 - Bad Request" error. -// -// See: https://jwt.io/introduction -// See `JWTConfig.TokenLookup` -// -// Deprecated: Please use https://github.com/labstack/echo-jwt instead -func JWT(key interface{}) echo.MiddlewareFunc { - c := DefaultJWTConfig - c.SigningKey = key - return JWTWithConfig(c) -} - -// JWTWithConfig returns a JWT auth middleware with config. -// See: `JWT()`. -// -// Deprecated: Please use https://github.com/labstack/echo-jwt instead -func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultJWTConfig.Skipper - } - if config.SigningKey == nil && len(config.SigningKeys) == 0 && config.KeyFunc == nil && config.ParseTokenFunc == nil { - panic("echo: jwt middleware requires signing key") - } - if config.SigningMethod == "" { - config.SigningMethod = DefaultJWTConfig.SigningMethod - } - if config.ContextKey == "" { - config.ContextKey = DefaultJWTConfig.ContextKey - } - if config.Claims == nil { - config.Claims = DefaultJWTConfig.Claims - } - if config.TokenLookup == "" && len(config.TokenLookupFuncs) == 0 { - config.TokenLookup = DefaultJWTConfig.TokenLookup - } - if config.AuthScheme == "" { - config.AuthScheme = DefaultJWTConfig.AuthScheme - } - if config.KeyFunc == nil { - config.KeyFunc = config.defaultKeyFunc - } - if config.ParseTokenFunc == nil { - config.ParseTokenFunc = config.defaultParseToken - } - - extractors, cErr := createExtractors(config.TokenLookup, config.AuthScheme) - if cErr != nil { - panic(cErr) - } - if len(config.TokenLookupFuncs) > 0 { - extractors = append(config.TokenLookupFuncs, extractors...) - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - if config.BeforeFunc != nil { - config.BeforeFunc(c) - } - - var lastExtractorErr error - var lastTokenErr error - for _, extractor := range extractors { - auths, err := extractor(c) - if err != nil { - lastExtractorErr = ErrJWTMissing // backwards compatibility: all extraction errors are same (unlike KeyAuth) - continue - } - for _, auth := range auths { - token, err := config.ParseTokenFunc(auth, c) - if err != nil { - lastTokenErr = err - continue - } - // Store user information from token into context. - c.Set(config.ContextKey, token) - if config.SuccessHandler != nil { - config.SuccessHandler(c) - } - return next(c) - } - } - // we are here only when we did not successfully extract or parse any of the tokens - err := lastTokenErr - if err == nil { // prioritize token errors over extracting errors - err = lastExtractorErr - } - if config.ErrorHandler != nil { - return config.ErrorHandler(err) - } - if config.ErrorHandlerWithContext != nil { - tmpErr := config.ErrorHandlerWithContext(err, c) - if config.ContinueOnIgnoredError && tmpErr == nil { - return next(c) - } - return tmpErr - } - - // backwards compatible errors codes - if lastTokenErr != nil { - return &echo.HTTPError{ - Code: ErrJWTInvalid.Code, - Message: ErrJWTInvalid.Message, - Internal: err, - } - } - return err // this is lastExtractorErr value - } - } -} - -func (config *JWTConfig) defaultParseToken(auth string, c echo.Context) (interface{}, error) { - var token *jwt.Token - var err error - // Issue #647, #656 - if _, ok := config.Claims.(jwt.MapClaims); ok { - token, err = jwt.Parse(auth, config.KeyFunc) - } else { - t := reflect.ValueOf(config.Claims).Type().Elem() - claims := reflect.New(t).Interface().(jwt.Claims) - token, err = jwt.ParseWithClaims(auth, claims, config.KeyFunc) - } - if err != nil { - return nil, err - } - if !token.Valid { - return nil, errors.New("invalid token") - } - return token, nil -} - -// defaultKeyFunc returns a signing key of the given token. -func (config *JWTConfig) defaultKeyFunc(t *jwt.Token) (interface{}, error) { - // Check the signing method - if t.Method.Alg() != config.SigningMethod { - return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"]) - } - if len(config.SigningKeys) > 0 { - if kid, ok := t.Header["kid"].(string); ok { - if key, ok := config.SigningKeys[kid]; ok { - return key, nil - } - } - return nil, fmt.Errorf("unexpected jwt key id=%v", t.Header["kid"]) - } - - return config.SigningKey, nil -} diff --git a/vendor/github.com/labstack/echo/v4/middleware/logger.go b/vendor/github.com/labstack/echo/v4/middleware/logger.go index 910fce8cf..59020955b 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/logger.go +++ b/vendor/github.com/labstack/echo/v4/middleware/logger.go @@ -5,7 +5,6 @@ package middleware import ( "bytes" - "encoding/json" "io" "strconv" "strings" @@ -18,60 +17,186 @@ import ( ) // LoggerConfig defines the config for Logger middleware. +// +// # Configuration Examples +// +// ## Basic Usage with Default Settings +// +// e.Use(middleware.Logger()) +// +// This uses the default JSON format that logs all common request/response details. +// +// ## Custom Simple Format +// +// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ +// Format: "${time_rfc3339_nano} ${status} ${method} ${uri} ${latency_human}\n", +// })) +// +// ## JSON Format with Custom Fields +// +// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ +// Format: `{"timestamp":"${time_rfc3339_nano}","level":"info","remote_ip":"${remote_ip}",` + +// `"method":"${method}","uri":"${uri}","status":${status},"latency":"${latency_human}",` + +// `"user_agent":"${user_agent}","error":"${error}"}` + "\n", +// })) +// +// ## Custom Time Format +// +// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ +// Format: "${time_custom} ${method} ${uri} ${status}\n", +// CustomTimeFormat: "2006-01-02 15:04:05", +// })) +// +// ## Logging Headers and Parameters +// +// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ +// Format: `{"time":"${time_rfc3339_nano}","method":"${method}","uri":"${uri}",` + +// `"status":${status},"auth":"${header:Authorization}","user":"${query:user}",` + +// `"form_data":"${form:action}","session":"${cookie:session_id}"}` + "\n", +// })) +// +// ## Custom Output (File Logging) +// +// file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) +// if err != nil { +// log.Fatal(err) +// } +// defer file.Close() +// +// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ +// Output: file, +// })) +// +// ## Custom Tag Function +// +// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ +// Format: `{"time":"${time_rfc3339_nano}","user_id":"${custom}","method":"${method}"}` + "\n", +// CustomTagFunc: func(c echo.Context, buf *bytes.Buffer) (int, error) { +// userID := getUserIDFromContext(c) // Your custom logic +// return buf.WriteString(strconv.Itoa(userID)) +// }, +// })) +// +// ## Conditional Logging (Skip Certain Requests) +// +// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ +// Skipper: func(c echo.Context) bool { +// // Skip logging for health check endpoints +// return c.Request().URL.Path == "/health" || c.Request().URL.Path == "/metrics" +// }, +// })) +// +// ## Integration with External Logging Service +// +// logBuffer := &SyncBuffer{} // Thread-safe buffer for external service +// +// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ +// Format: `{"timestamp":"${time_rfc3339_nano}","service":"my-api","level":"info",` + +// `"method":"${method}","uri":"${uri}","status":${status},"latency_ms":${latency},` + +// `"remote_ip":"${remote_ip}","user_agent":"${user_agent}","error":"${error}"}` + "\n", +// Output: logBuffer, +// })) +// +// # Available Tags +// +// ## Time Tags +// - time_unix: Unix timestamp (seconds) +// - time_unix_milli: Unix timestamp (milliseconds) +// - time_unix_micro: Unix timestamp (microseconds) +// - time_unix_nano: Unix timestamp (nanoseconds) +// - time_rfc3339: RFC3339 format (2006-01-02T15:04:05Z07:00) +// - time_rfc3339_nano: RFC3339 with nanoseconds +// - time_custom: Uses CustomTimeFormat field +// +// ## Request Information +// - id: Request ID from X-Request-ID header +// - remote_ip: Client IP address (respects proxy headers) +// - uri: Full request URI with query parameters +// - host: Host header value +// - method: HTTP method (GET, POST, etc.) +// - path: URL path without query parameters +// - route: Echo route pattern (e.g., /users/:id) +// - protocol: HTTP protocol version +// - referer: Referer header value +// - user_agent: User-Agent header value +// +// ## Response Information +// - status: HTTP status code +// - error: Error message if request failed +// - latency: Request processing time in nanoseconds +// - latency_human: Human-readable processing time +// - bytes_in: Request body size in bytes +// - bytes_out: Response body size in bytes +// +// ## Dynamic Tags +// - header:: Value of specific header (e.g., header:Authorization) +// - query:: Value of specific query parameter (e.g., query:user_id) +// - form:: Value of specific form field (e.g., form:username) +// - cookie:: Value of specific cookie (e.g., cookie:session_id) +// - custom: Output from CustomTagFunc +// +// # Troubleshooting +// +// ## Common Issues +// +// 1. **Missing logs**: Check if Skipper function is filtering out requests +// 2. **Invalid JSON**: Ensure CustomTagFunc outputs valid JSON content +// 3. **Performance issues**: Consider using a buffered writer for high-traffic applications +// 4. **File permission errors**: Ensure write permissions when logging to files +// +// ## Performance Tips +// +// - Use time_unix formats for better performance than time_rfc3339 +// - Minimize the number of dynamic tags (header:, query:, form:, cookie:) +// - Use Skipper to exclude high-frequency, low-value requests (health checks, etc.) +// - Consider async logging for very high-traffic applications type LoggerConfig struct { // Skipper defines a function to skip middleware. + // Use this to exclude certain requests from logging (e.g., health checks). + // + // Example: + // Skipper: func(c echo.Context) bool { + // return c.Request().URL.Path == "/health" + // }, Skipper Skipper - // Tags to construct the logger format. - // - // - time_unix - // - time_unix_milli - // - time_unix_micro - // - time_unix_nano - // - time_rfc3339 - // - time_rfc3339_nano - // - time_custom - // - id (Request ID) - // - remote_ip - // - uri - // - host - // - method - // - path - // - route - // - protocol - // - referer - // - user_agent - // - status - // - error - // - latency (In nanoseconds) - // - latency_human (Human readable) - // - bytes_in (Bytes received) - // - bytes_out (Bytes sent) - // - header: - // - query: - // - form: - // - custom (see CustomTagFunc field) - // - // Example "${remote_ip} ${status}" + // Format defines the logging format using template tags. + // Tags are enclosed in ${} and replaced with actual values. + // See the detailed tag documentation above for all available options. // - // Optional. Default value DefaultLoggerConfig.Format. + // Default: JSON format with common fields + // Example: "${time_rfc3339_nano} ${status} ${method} ${uri} ${latency_human}\n" Format string `yaml:"format"` - // Optional. Default value DefaultLoggerConfig.CustomTimeFormat. + // CustomTimeFormat specifies the time format used by ${time_custom} tag. + // Uses Go's reference time: Mon Jan 2 15:04:05 MST 2006 + // + // Default: "2006-01-02 15:04:05.00000" + // Example: "2006-01-02 15:04:05" or "15:04:05.000" CustomTimeFormat string `yaml:"custom_time_format"` - // CustomTagFunc is function called for `${custom}` tag to output user implemented text by writing it to buf. - // Make sure that outputted text creates valid JSON string with other logged tags. - // Optional. + // CustomTagFunc is called when ${custom} tag is encountered. + // Use this to add application-specific information to logs. + // The function should write valid content for your log format. + // + // Example: + // CustomTagFunc: func(c echo.Context, buf *bytes.Buffer) (int, error) { + // userID := getUserFromContext(c) + // return buf.WriteString(`"user_id":"` + userID + `"`) + // }, CustomTagFunc func(c echo.Context, buf *bytes.Buffer) (int, error) - // Output is a writer where logs in JSON format are written. - // Optional. Default value os.Stdout. + // Output specifies where logs are written. + // Can be any io.Writer: files, buffers, network connections, etc. + // + // Default: os.Stdout + // Example: Custom file, syslog, or external logging service Output io.Writer template *fasttemplate.Template colorer *color.Color pool *sync.Pool + timeNow func() time.Time } // DefaultLoggerConfig is the default Logger middleware config. @@ -83,15 +208,62 @@ var DefaultLoggerConfig = LoggerConfig{ `,"bytes_in":${bytes_in},"bytes_out":${bytes_out}}` + "\n", CustomTimeFormat: "2006-01-02 15:04:05.00000", colorer: color.New(), + timeNow: time.Now, } -// Logger returns a middleware that logs HTTP requests. +// Logger returns a middleware that logs HTTP requests using the default configuration. +// +// The default format logs requests as JSON with the following fields: +// - time: RFC3339 nano timestamp +// - id: Request ID from X-Request-ID header +// - remote_ip: Client IP address +// - host: Host header +// - method: HTTP method +// - uri: Request URI +// - user_agent: User-Agent header +// - status: HTTP status code +// - error: Error message (if any) +// - latency: Processing time in nanoseconds +// - latency_human: Human-readable processing time +// - bytes_in: Request body size +// - bytes_out: Response body size +// +// Example output: +// +// {"time":"2023-01-15T10:30:45.123456789Z","id":"","remote_ip":"127.0.0.1", +// "host":"localhost:8080","method":"GET","uri":"/users/123","user_agent":"curl/7.81.0", +// "status":200,"error":"","latency":1234567,"latency_human":"1.234567ms", +// "bytes_in":0,"bytes_out":42} +// +// For custom configurations, use LoggerWithConfig instead. +// +// Deprecated: please use middleware.RequestLogger or middleware.RequestLoggerWithConfig instead. func Logger() echo.MiddlewareFunc { return LoggerWithConfig(DefaultLoggerConfig) } -// LoggerWithConfig returns a Logger middleware with config. -// See: `Logger()`. +// LoggerWithConfig returns a Logger middleware with custom configuration. +// +// This function allows you to customize all aspects of request logging including: +// - Log format and fields +// - Output destination +// - Time formatting +// - Custom tags and logic +// - Request filtering +// +// See LoggerConfig documentation for detailed configuration examples and options. +// +// Example: +// +// e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ +// Format: "${time_rfc3339} ${status} ${method} ${uri} ${latency_human}\n", +// Output: customLogWriter, +// Skipper: func(c echo.Context) bool { +// return c.Request().URL.Path == "/health" +// }, +// })) +// +// Deprecated: please use middleware.RequestLoggerWithConfig instead. func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { // Defaults if config.Skipper == nil { @@ -100,9 +272,18 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { if config.Format == "" { config.Format = DefaultLoggerConfig.Format } + writeString := func(buf *bytes.Buffer, in string) (int, error) { return buf.WriteString(in) } + if config.Format[0] == '{' { // format looks like JSON, so we need to escape invalid characters + writeString = writeJSONSafeString + } + if config.Output == nil { config.Output = DefaultLoggerConfig.Output } + timeNow := DefaultLoggerConfig.timeNow + if config.timeNow != nil { + timeNow = config.timeNow + } config.template = fasttemplate.New(config.Format, "${", "}") config.colorer = color.New() @@ -138,49 +319,47 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { } return config.CustomTagFunc(c, buf) case "time_unix": - return buf.WriteString(strconv.FormatInt(time.Now().Unix(), 10)) + return buf.WriteString(strconv.FormatInt(timeNow().Unix(), 10)) case "time_unix_milli": - // go 1.17 or later, it supports time#UnixMilli() - return buf.WriteString(strconv.FormatInt(time.Now().UnixNano()/1000000, 10)) + return buf.WriteString(strconv.FormatInt(timeNow().UnixMilli(), 10)) case "time_unix_micro": - // go 1.17 or later, it supports time#UnixMicro() - return buf.WriteString(strconv.FormatInt(time.Now().UnixNano()/1000, 10)) + return buf.WriteString(strconv.FormatInt(timeNow().UnixMicro(), 10)) case "time_unix_nano": - return buf.WriteString(strconv.FormatInt(time.Now().UnixNano(), 10)) + return buf.WriteString(strconv.FormatInt(timeNow().UnixNano(), 10)) case "time_rfc3339": - return buf.WriteString(time.Now().Format(time.RFC3339)) + return buf.WriteString(timeNow().Format(time.RFC3339)) case "time_rfc3339_nano": - return buf.WriteString(time.Now().Format(time.RFC3339Nano)) + return buf.WriteString(timeNow().Format(time.RFC3339Nano)) case "time_custom": - return buf.WriteString(time.Now().Format(config.CustomTimeFormat)) + return buf.WriteString(timeNow().Format(config.CustomTimeFormat)) case "id": id := req.Header.Get(echo.HeaderXRequestID) if id == "" { id = res.Header().Get(echo.HeaderXRequestID) } - return buf.WriteString(id) + return writeString(buf, id) case "remote_ip": - return buf.WriteString(c.RealIP()) + return writeString(buf, c.RealIP()) case "host": - return buf.WriteString(req.Host) + return writeString(buf, req.Host) case "uri": - return buf.WriteString(req.RequestURI) + return writeString(buf, req.RequestURI) case "method": - return buf.WriteString(req.Method) + return writeString(buf, req.Method) case "path": p := req.URL.Path if p == "" { p = "/" } - return buf.WriteString(p) + return writeString(buf, p) case "route": - return buf.WriteString(c.Path()) + return writeString(buf, c.Path()) case "protocol": - return buf.WriteString(req.Proto) + return writeString(buf, req.Proto) case "referer": - return buf.WriteString(req.Referer()) + return writeString(buf, req.Referer()) case "user_agent": - return buf.WriteString(req.UserAgent()) + return writeString(buf, req.UserAgent()) case "status": n := res.Status s := config.colorer.Green(n) @@ -195,10 +374,7 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { return buf.WriteString(s) case "error": if err != nil { - // Error may contain invalid JSON e.g. `"` - b, _ := json.Marshal(err.Error()) - b = b[1 : len(b)-1] - return buf.Write(b) + return writeJSONSafeString(buf, err.Error()) } case "latency": l := stop.Sub(start) @@ -210,17 +386,17 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { if cl == "" { cl = "0" } - return buf.WriteString(cl) + return writeString(buf, cl) case "bytes_out": return buf.WriteString(strconv.FormatInt(res.Size, 10)) default: switch { case strings.HasPrefix(tag, "header:"): - return buf.Write([]byte(c.Request().Header.Get(tag[7:]))) + return writeString(buf, c.Request().Header.Get(tag[7:])) case strings.HasPrefix(tag, "query:"): - return buf.Write([]byte(c.QueryParam(tag[6:]))) + return writeString(buf, c.QueryParam(tag[6:])) case strings.HasPrefix(tag, "form:"): - return buf.Write([]byte(c.FormValue(tag[5:]))) + return writeString(buf, c.FormValue(tag[5:])) case strings.HasPrefix(tag, "cookie:"): cookie, err := c.Cookie(tag[7:]) if err == nil { diff --git a/vendor/github.com/labstack/echo/v4/middleware/logger_strings.go b/vendor/github.com/labstack/echo/v4/middleware/logger_strings.go new file mode 100644 index 000000000..8476cb046 --- /dev/null +++ b/vendor/github.com/labstack/echo/v4/middleware/logger_strings.go @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: Copyright 2010 The Go Authors +// +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +// Go LICENSE https://raw.githubusercontent.com/golang/go/36bca3166e18db52687a4d91ead3f98ffe6d00b8/LICENSE +/** +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package middleware + +import ( + "bytes" + "unicode/utf8" +) + +// This function is modified copy from Go standard library encoding/json/encode.go `appendString` function +// Source: https://github.com/golang/go/blob/36bca3166e18db52687a4d91ead3f98ffe6d00b8/src/encoding/json/encode.go#L999 +func writeJSONSafeString(buf *bytes.Buffer, src string) (int, error) { + const hex = "0123456789abcdef" + + written := 0 + start := 0 + for i := 0; i < len(src); { + if b := src[i]; b < utf8.RuneSelf { + if safeSet[b] { + i++ + continue + } + + n, err := buf.Write([]byte(src[start:i])) + written += n + if err != nil { + return written, err + } + switch b { + case '\\', '"': + n, err := buf.Write([]byte{'\\', b}) + written += n + if err != nil { + return written, err + } + case '\b': + n, err := buf.Write([]byte{'\\', 'b'}) + written += n + if err != nil { + return n, err + } + case '\f': + n, err := buf.Write([]byte{'\\', 'f'}) + written += n + if err != nil { + return written, err + } + case '\n': + n, err := buf.Write([]byte{'\\', 'n'}) + written += n + if err != nil { + return written, err + } + case '\r': + n, err := buf.Write([]byte{'\\', 'r'}) + written += n + if err != nil { + return written, err + } + case '\t': + n, err := buf.Write([]byte{'\\', 't'}) + written += n + if err != nil { + return written, err + } + default: + // This encodes bytes < 0x20 except for \b, \f, \n, \r and \t. + n, err := buf.Write([]byte{'\\', 'u', '0', '0', hex[b>>4], hex[b&0xF]}) + written += n + if err != nil { + return written, err + } + } + i++ + start = i + continue + } + srcN := min(len(src)-i, utf8.UTFMax) + c, size := utf8.DecodeRuneInString(src[i : i+srcN]) + if c == utf8.RuneError && size == 1 { + n, err := buf.Write([]byte(src[start:i])) + written += n + if err != nil { + return written, err + } + n, err = buf.Write([]byte(`\ufffd`)) + written += n + if err != nil { + return written, err + } + i += size + start = i + continue + } + i += size + } + n, err := buf.Write([]byte(src[start:])) + written += n + return written, err +} + +// safeSet holds the value true if the ASCII character with the given array +// position can be represented inside a JSON string without any further +// escaping. +// +// All values are true except for the ASCII control characters (0-31), the +// double quote ("), and the backslash character ("\"). +var safeSet = [utf8.RuneSelf]bool{ + ' ': true, + '!': true, + '"': false, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '(': true, + ')': true, + '*': true, + '+': true, + ',': true, + '-': true, + '.': true, + '/': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + ':': true, + ';': true, + '<': true, + '=': true, + '>': true, + '?': true, + '@': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'V': true, + 'W': true, + 'X': true, + 'Y': true, + 'Z': true, + '[': true, + '\\': false, + ']': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '{': true, + '|': true, + '}': true, + '~': true, + '\u007f': true, +} diff --git a/vendor/github.com/labstack/echo/v4/middleware/middleware.go b/vendor/github.com/labstack/echo/v4/middleware/middleware.go index 6f33cc5c1..164e52b4c 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/middleware.go +++ b/vendor/github.com/labstack/echo/v4/middleware/middleware.go @@ -88,3 +88,13 @@ func rewriteURL(rewriteRegex map[*regexp.Regexp]string, req *http.Request) error func DefaultSkipper(echo.Context) bool { return false } + +func toMiddlewareOrPanic(config interface { + ToMiddleware() (echo.MiddlewareFunc, error) +}) echo.MiddlewareFunc { + mw, err := config.ToMiddleware() + if err != nil { + panic(err) + } + return mw +} diff --git a/vendor/github.com/labstack/echo/v4/middleware/proxy.go b/vendor/github.com/labstack/echo/v4/middleware/proxy.go index 495970aca..f26870077 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/proxy.go +++ b/vendor/github.com/labstack/echo/v4/middleware/proxy.go @@ -5,6 +5,7 @@ package middleware import ( "context" + "crypto/tls" "fmt" "io" "math/rand" @@ -130,7 +131,21 @@ var DefaultProxyConfig = ProxyConfig{ ContextKey: "target", } -func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { +func proxyRaw(t *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler { + var dialFunc func(ctx context.Context, network, addr string) (net.Conn, error) + if transport, ok := config.Transport.(*http.Transport); ok { + if transport.TLSClientConfig != nil { + d := tls.Dialer{ + Config: transport.TLSClientConfig, + } + dialFunc = d.DialContext + } + } + if dialFunc == nil { + var d net.Dialer + dialFunc = d.DialContext + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { in, _, err := c.Response().Hijack() if err != nil { @@ -138,8 +153,7 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { return } defer in.Close() - - out, err := net.Dial("tcp", t.URL.Host) + out, err := dialFunc(c.Request().Context(), "tcp", t.URL.Host) if err != nil { c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", err, t.URL))) return @@ -155,15 +169,21 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { errCh := make(chan error, 2) cp := func(dst io.Writer, src io.Reader) { - _, err = io.Copy(dst, src) - errCh <- err + _, copyErr := io.Copy(dst, src) + errCh <- copyErr } go cp(out, in) go cp(in, out) - err = <-errCh - if err != nil && err != io.EOF { - c.Set("_error", fmt.Errorf("proxy raw, copy body error=%w, url=%s", err, t.URL)) + + // Wait for BOTH goroutines to complete + err1 := <-errCh + err2 := <-errCh + + if err1 != nil && err1 != io.EOF { + c.Set("_error", fmt.Errorf("proxy raw, copy body error=%w, url=%s", err1, t.URL)) + } else if err2 != nil && err2 != io.EOF { + c.Set("_error", fmt.Errorf("proxy raw, copy body error=%w, url=%s", err2, t.URL)) } }) } @@ -365,7 +385,7 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc { // Proxy switch { case c.IsWebSocket(): - proxyRaw(tgt, c).ServeHTTP(res, req) + proxyRaw(tgt, c, config).ServeHTTP(res, req) default: // even SSE requests proxyHTTP(tgt, c, config).ServeHTTP(res, req) } diff --git a/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go b/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go index d4724fd2a..2746a3de1 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go +++ b/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go @@ -4,6 +4,7 @@ package middleware import ( + "math" "net/http" "sync" "time" @@ -191,7 +192,7 @@ NewRateLimiterMemoryStoreWithConfig returns an instance of RateLimiterMemoryStor with the provided configuration. Rate must be provided. Burst will be set to the rounded down value of the configured rate if not provided or set to 0. -The build-in memory store is usually capable for modest loads. For higher loads other +The built-in memory store is usually capable for modest loads. For higher loads other store implementations should be considered. Characteristics: @@ -215,7 +216,7 @@ func NewRateLimiterMemoryStoreWithConfig(config RateLimiterMemoryStoreConfig) (s store.expiresIn = DefaultRateLimiterMemoryStoreConfig.ExpiresIn } if config.Burst == 0 { - store.burst = int(config.Rate) + store.burst = int(math.Max(1, math.Ceil(float64(config.Rate)))) } store.visitors = make(map[string]*Visitor) store.timeNow = time.Now @@ -249,8 +250,9 @@ func (store *RateLimiterMemoryStore) Allow(identifier string) (bool, error) { if now.Sub(store.lastCleanup) > store.expiresIn { store.cleanupStaleVisitors() } + allowed := limiter.AllowN(now, 1) store.mutex.Unlock() - return limiter.AllowN(store.timeNow(), 1), nil + return allowed, nil } /* diff --git a/vendor/github.com/labstack/echo/v4/middleware/request_logger.go b/vendor/github.com/labstack/echo/v4/middleware/request_logger.go index 7c18200b0..211abf464 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/request_logger.go +++ b/vendor/github.com/labstack/echo/v4/middleware/request_logger.go @@ -4,7 +4,9 @@ package middleware import ( + "context" "errors" + "log/slog" "net/http" "time" @@ -247,6 +249,72 @@ func RequestLoggerWithConfig(config RequestLoggerConfig) echo.MiddlewareFunc { return mw } +// RequestLogger returns a RequestLogger middleware with default configuration which +// uses default slog.slog logger. +// +// To customize slog output format replace slog default logger: +// For JSON format: `slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil)))` +func RequestLogger() echo.MiddlewareFunc { + config := RequestLoggerConfig{ + LogLatency: true, + LogProtocol: false, + LogRemoteIP: true, + LogHost: true, + LogMethod: true, + LogURI: true, + LogURIPath: false, + LogRoutePath: false, + LogRequestID: true, + LogReferer: false, + LogUserAgent: true, + LogStatus: true, + LogError: true, + LogContentLength: true, + LogResponseSize: true, + LogHeaders: nil, + LogQueryParams: nil, + LogFormValues: nil, + HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code + LogValuesFunc: func(c echo.Context, v RequestLoggerValues) error { + if v.Error == nil { + slog.LogAttrs(context.Background(), slog.LevelInfo, "REQUEST", + slog.String("method", v.Method), + slog.String("uri", v.URI), + slog.Int("status", v.Status), + slog.Duration("latency", v.Latency), + slog.String("host", v.Host), + slog.String("bytes_in", v.ContentLength), + slog.Int64("bytes_out", v.ResponseSize), + slog.String("user_agent", v.UserAgent), + slog.String("remote_ip", v.RemoteIP), + slog.String("request_id", v.RequestID), + ) + } else { + slog.LogAttrs(context.Background(), slog.LevelError, "REQUEST_ERROR", + slog.String("method", v.Method), + slog.String("uri", v.URI), + slog.Int("status", v.Status), + slog.Duration("latency", v.Latency), + slog.String("host", v.Host), + slog.String("bytes_in", v.ContentLength), + slog.Int64("bytes_out", v.ResponseSize), + slog.String("user_agent", v.UserAgent), + slog.String("remote_ip", v.RemoteIP), + slog.String("request_id", v.RequestID), + + slog.String("error", v.Error.Error()), + ) + } + return nil + }, + } + mw, err := config.ToMiddleware() + if err != nil { + panic(err) + } + return mw +} + // ToMiddleware converts RequestLoggerConfig into middleware or returns an error for invalid configuration. func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) { if config.Skipper == nil { diff --git a/vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.19.go b/vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.19.go deleted file mode 100644 index ddf6b64c0..000000000 --- a/vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.19.go +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors - -//go:build !go1.20 - -package middleware - -import ( - "bufio" - "fmt" - "net" - "net/http" -) - -// TODO: remove when Go 1.23 is released and we do not support 1.19 anymore -func responseControllerFlush(rw http.ResponseWriter) error { - for { - switch t := rw.(type) { - case interface{ FlushError() error }: - return t.FlushError() - case http.Flusher: - t.Flush() - return nil - case interface{ Unwrap() http.ResponseWriter }: - rw = t.Unwrap() - default: - return fmt.Errorf("%w", http.ErrNotSupported) - } - } -} - -// TODO: remove when Go 1.23 is released and we do not support 1.19 anymore -func responseControllerHijack(rw http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) { - for { - switch t := rw.(type) { - case http.Hijacker: - return t.Hijack() - case interface{ Unwrap() http.ResponseWriter }: - rw = t.Unwrap() - default: - return nil, nil, fmt.Errorf("%w", http.ErrNotSupported) - } - } -} diff --git a/vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.20.go b/vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.20.go deleted file mode 100644 index bc03059bc..000000000 --- a/vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.20.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors - -//go:build go1.20 - -package middleware - -import ( - "bufio" - "net" - "net/http" -) - -func responseControllerFlush(rw http.ResponseWriter) error { - return http.NewResponseController(rw).Flush() -} - -func responseControllerHijack(rw http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) { - return http.NewResponseController(rw).Hijack() -} diff --git a/vendor/github.com/labstack/echo/v4/middleware/static.go b/vendor/github.com/labstack/echo/v4/middleware/static.go index 1016f1b09..16a983896 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/static.go +++ b/vendor/github.com/labstack/echo/v4/middleware/static.go @@ -48,6 +48,15 @@ type StaticConfig struct { // Filesystem provides access to the static content. // Optional. Defaults to http.Dir(config.Root) Filesystem http.FileSystem `yaml:"-"` + + // EnablePathUnescaping enables path parameter (param: *) unescaping. + // Default false (safe): encoded slashes (%2f) in the wildcard param are NOT decoded, + // preventing ACL bypass where /admin%2fprivate.txt bypasses a /admin/* route guard by + // not matching that route but having its wildcard param decoded to admin/private.txt. + // Set to true only when serving files whose names contain URL-encoded characters + // (e.g. "hello world.txt" via /hello%20world.txt) and you are not relying on + // route-based ACL guards to restrict access. + EnablePathUnescaping bool `yaml:"enablePathUnescaping"` } const html = ` @@ -170,10 +179,18 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc { if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`. p = c.Param("*") } - p, err = url.PathUnescape(p) - if err != nil { - return + if config.EnablePathUnescaping { + p, err = url.PathUnescape(p) + if err != nil { + return + } } + // Security: We use path.Clean() (not filepath.Clean()) because: + // 1. HTTP URLs always use forward slashes, regardless of server OS + // 2. path.Clean() provides platform-independent behavior for URL paths + // 3. The "/" prefix forces absolute path interpretation, removing ".." components + // 4. Backslashes are treated as literal characters (not path separators), preventing traversal + // See static_windows.go for Go 1.20+ filepath.Clean compatibility notes name := path.Join(config.Root, path.Clean("/"+p)) // "/"+ for security if config.IgnoreBase { diff --git a/vendor/github.com/labstack/echo/v4/middleware/timeout.go b/vendor/github.com/labstack/echo/v4/middleware/timeout.go index c2aebef30..c0a77a4b0 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/timeout.go +++ b/vendor/github.com/labstack/echo/v4/middleware/timeout.go @@ -59,6 +59,12 @@ import ( // // TimeoutConfig defines the config for Timeout middleware. +// +// Deprecated: Use ContextTimeoutConfig with ContextTimeout or ContextTimeoutWithConfig instead. +// The Timeout middleware has architectural issues that cause data races due to response writer +// manipulation across goroutines. It must be the first middleware in the chain, making it fragile. +// The ContextTimeout middleware provides timeout functionality using Go's context mechanism, +// which is race-free and can be placed anywhere in the middleware chain. type TimeoutConfig struct { // Skipper defines a function to skip middleware. Skipper Skipper @@ -89,11 +95,38 @@ var DefaultTimeoutConfig = TimeoutConfig{ // Timeout returns a middleware which returns error (503 Service Unavailable error) to client immediately when handler // call runs for longer than its time limit. NB: timeout does not stop handler execution. +// +// Deprecated: Use ContextTimeout instead. This middleware has known data race issues due to response writer +// manipulation. See https://github.com/labstack/echo/blob/master/middleware/context_timeout.go for the +// recommended alternative. +// +// Example migration: +// +// // Before: +// e.Use(middleware.Timeout()) +// +// // After: +// e.Use(middleware.ContextTimeout(30 * time.Second)) func Timeout() echo.MiddlewareFunc { return TimeoutWithConfig(DefaultTimeoutConfig) } // TimeoutWithConfig returns a Timeout middleware with config or panics on invalid configuration. +// +// Deprecated: Use ContextTimeoutWithConfig instead. This middleware has architectural data race issues. +// See the ContextTimeout middleware for a race-free alternative that uses Go's context mechanism. +// +// Example migration: +// +// // Before: +// e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{ +// Timeout: 30 * time.Second, +// })) +// +// // After: +// e.Use(middleware.ContextTimeoutWithConfig(middleware.ContextTimeoutConfig{ +// Timeout: 30 * time.Second, +// })) func TimeoutWithConfig(config TimeoutConfig) echo.MiddlewareFunc { mw, err := config.ToMiddleware() if err != nil { @@ -103,6 +136,8 @@ func TimeoutWithConfig(config TimeoutConfig) echo.MiddlewareFunc { } // ToMiddleware converts Config to middleware or returns an error for invalid configuration +// +// Deprecated: Use ContextTimeoutConfig.ToMiddleware instead. func (config TimeoutConfig) ToMiddleware() (echo.MiddlewareFunc, error) { if config.Skipper == nil { config.Skipper = DefaultTimeoutConfig.Skipper diff --git a/vendor/github.com/labstack/echo/v4/middleware/util.go b/vendor/github.com/labstack/echo/v4/middleware/util.go index 09428eb0b..5813990a5 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/util.go +++ b/vendor/github.com/labstack/echo/v4/middleware/util.go @@ -6,7 +6,9 @@ package middleware import ( "bufio" "crypto/rand" + "fmt" "io" + "net/url" "strings" "sync" ) @@ -101,3 +103,26 @@ func randomString(length uint8) string { } } } + +func validateOrigins(origins []string, what string) error { + for _, o := range origins { + if err := validateOrigin(o, what); err != nil { + return err + } + } + return nil +} + +func validateOrigin(origin string, what string) error { + u, err := url.Parse(origin) + if err != nil { + return fmt.Errorf("can not parse %s: %w", what, err) + } + if u.Scheme == "" || u.Host == "" { + return fmt.Errorf("%s is missing scheme or host: %s", what, origin) + } + if u.Path != "" || u.RawQuery != "" || u.Fragment != "" { + return fmt.Errorf("%s can not have path, query, and fragments: %s", what, origin) + } + return nil +} diff --git a/vendor/github.com/labstack/echo/v4/renderer.go b/vendor/github.com/labstack/echo/v4/renderer.go new file mode 100644 index 000000000..44e038f35 --- /dev/null +++ b/vendor/github.com/labstack/echo/v4/renderer.go @@ -0,0 +1,29 @@ +package echo + +import "io" + +// Renderer is the interface that wraps the Render function. +type Renderer interface { + Render(io.Writer, string, interface{}, Context) error +} + +// TemplateRenderer is helper to ease creating renderers for `html/template` and `text/template` packages. +// Example usage: +// +// e.Renderer = &echo.TemplateRenderer{ +// Template: template.Must(template.ParseGlob("templates/*.html")), +// } +// +// e.Renderer = &echo.TemplateRenderer{ +// Template: template.Must(template.New("hello").Parse("Hello, {{.}}!")), +// } +type TemplateRenderer struct { + Template interface { + ExecuteTemplate(wr io.Writer, name string, data any) error + } +} + +// Render renders the template with given data. +func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c Context) error { + return t.Template.ExecuteTemplate(w, name, data) +} diff --git a/vendor/github.com/labstack/echo/v4/response.go b/vendor/github.com/labstack/echo/v4/response.go index a795ce36e..0c61c9735 100644 --- a/vendor/github.com/labstack/echo/v4/response.go +++ b/vendor/github.com/labstack/echo/v4/response.go @@ -6,6 +6,7 @@ package echo import ( "bufio" "errors" + "fmt" "net" "net/http" ) @@ -14,10 +15,10 @@ import ( // by an HTTP handler to construct an HTTP response. // See: https://golang.org/pkg/net/http/#ResponseWriter type Response struct { + Writer http.ResponseWriter echo *Echo beforeFuncs []func() afterFuncs []func() - Writer http.ResponseWriter Status int Size int64 Committed bool @@ -86,9 +87,9 @@ func (r *Response) Write(b []byte) (n int, err error) { // buffered data to the client. // See [http.Flusher](https://golang.org/pkg/net/http/#Flusher) func (r *Response) Flush() { - err := responseControllerFlush(r.Writer) + err := http.NewResponseController(r.Writer).Flush() if err != nil && errors.Is(err, http.ErrNotSupported) { - panic(errors.New("response writer flushing is not supported")) + panic(fmt.Errorf("echo: response writer %T does not support flushing (http.Flusher interface)", r.Writer)) } } @@ -96,7 +97,7 @@ func (r *Response) Flush() { // take over the connection. // See [http.Hijacker](https://golang.org/pkg/net/http/#Hijacker) func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return responseControllerHijack(r.Writer) + return http.NewResponseController(r.Writer).Hijack() } // Unwrap returns the original http.ResponseWriter. diff --git a/vendor/github.com/labstack/echo/v4/responsecontroller_1.19.go b/vendor/github.com/labstack/echo/v4/responsecontroller_1.19.go deleted file mode 100644 index 782dab3a3..000000000 --- a/vendor/github.com/labstack/echo/v4/responsecontroller_1.19.go +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors - -//go:build !go1.20 - -package echo - -import ( - "bufio" - "fmt" - "net" - "net/http" -) - -// TODO: remove when Go 1.23 is released and we do not support 1.19 anymore -func responseControllerFlush(rw http.ResponseWriter) error { - for { - switch t := rw.(type) { - case interface{ FlushError() error }: - return t.FlushError() - case http.Flusher: - t.Flush() - return nil - case interface{ Unwrap() http.ResponseWriter }: - rw = t.Unwrap() - default: - return fmt.Errorf("%w", http.ErrNotSupported) - } - } -} - -// TODO: remove when Go 1.23 is released and we do not support 1.19 anymore -func responseControllerHijack(rw http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) { - for { - switch t := rw.(type) { - case http.Hijacker: - return t.Hijack() - case interface{ Unwrap() http.ResponseWriter }: - rw = t.Unwrap() - default: - return nil, nil, fmt.Errorf("%w", http.ErrNotSupported) - } - } -} diff --git a/vendor/github.com/labstack/echo/v4/responsecontroller_1.20.go b/vendor/github.com/labstack/echo/v4/responsecontroller_1.20.go deleted file mode 100644 index 6d77c07f8..000000000 --- a/vendor/github.com/labstack/echo/v4/responsecontroller_1.20.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors - -//go:build go1.20 - -package echo - -import ( - "bufio" - "net" - "net/http" -) - -func responseControllerFlush(rw http.ResponseWriter) error { - return http.NewResponseController(rw).Flush() -} - -func responseControllerHijack(rw http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) { - return http.NewResponseController(rw).Hijack() -} diff --git a/vendor/github.com/labstack/echo/v4/router.go b/vendor/github.com/labstack/echo/v4/router.go index 03267315d..912cfeac0 100644 --- a/vendor/github.com/labstack/echo/v4/router.go +++ b/vendor/github.com/labstack/echo/v4/router.go @@ -18,32 +18,31 @@ type Router struct { } type node struct { - kind kind - label byte - prefix string - parent *node - staticChildren children - originalPath string - methods *routeMethods - paramChild *node - anyChild *node - paramsCount int + methods *routeMethods + parent *node + paramChild *node + anyChild *node + // notFoundHandler is handler registered with RouteNotFound method and is executed for 404 cases + notFoundHandler *routeMethod + prefix string + originalPath string + staticChildren children + paramsCount int + label byte + kind kind // isLeaf indicates that node does not have child routes isLeaf bool // isHandler indicates that node has at least one handler registered to it isHandler bool - - // notFoundHandler is handler registered with RouteNotFound method and is executed for 404 cases - notFoundHandler *routeMethod } type kind uint8 type children []*node type routeMethod struct { + handler HandlerFunc ppath string pnames []string - handler HandlerFunc } type routeMethods struct { @@ -242,18 +241,18 @@ func (r *Router) insert(method, path string, h HandlerFunc) { if i == lcpIndex { // path node is last fragment of route path. ie. `/users/:id` - r.insertNode(method, path[:i], paramKind, routeMethod{ppath, pnames, h}) + r.insertNode(method, path[:i], paramKind, routeMethod{ppath: ppath, pnames: pnames, handler: h}) } else { r.insertNode(method, path[:i], paramKind, routeMethod{}) } } else if path[i] == '*' { r.insertNode(method, path[:i], staticKind, routeMethod{}) pnames = append(pnames, "*") - r.insertNode(method, path[:i+1], anyKind, routeMethod{ppath, pnames, h}) + r.insertNode(method, path[:i+1], anyKind, routeMethod{ppath: ppath, pnames: pnames, handler: h}) } } - r.insertNode(method, path, staticKind, routeMethod{ppath, pnames, h}) + r.insertNode(method, path, staticKind, routeMethod{ppath: ppath, pnames: pnames, handler: h}) } func (r *Router) insertNode(method, path string, t kind, rm routeMethod) { @@ -693,7 +692,7 @@ func (r *Router) Find(method, path string, c Context) { // update indexes/search in case we need to backtrack when no handler match is found paramIndex++ - searchIndex += +len(search) + searchIndex += len(search) search = "" if h := currentNode.findMethod(method); h != nil { diff --git a/vendor/github.com/labstack/gommon/log/log.go b/vendor/github.com/labstack/gommon/log/log.go index 25f719aa2..a22335186 100644 --- a/vendor/github.com/labstack/gommon/log/log.go +++ b/vendor/github.com/labstack/gommon/log/log.go @@ -138,93 +138,94 @@ func (l *Logger) SetHeader(h string) { } func (l *Logger) Print(i ...interface{}) { - l.log(0, "", i...) - // fmt.Fprintln(l.output, i...) + l.log(0, fmt.Sprint(i...), false) } func (l *Logger) Printf(format string, args ...interface{}) { - l.log(0, format, args...) + l.log(0, fmt.Sprintf(format, args...), false) } func (l *Logger) Printj(j JSON) { - l.log(0, "json", j) + l.logJSON(0, j) } func (l *Logger) Debug(i ...interface{}) { - l.log(DEBUG, "", i...) + l.log(DEBUG, fmt.Sprint(i...), false) } func (l *Logger) Debugf(format string, args ...interface{}) { - l.log(DEBUG, format, args...) + l.log(DEBUG, fmt.Sprintf(format, args...), false) } func (l *Logger) Debugj(j JSON) { - l.log(DEBUG, "json", j) + l.logJSON(DEBUG, j) } func (l *Logger) Info(i ...interface{}) { - l.log(INFO, "", i...) + l.log(INFO, fmt.Sprint(i...), false) } func (l *Logger) Infof(format string, args ...interface{}) { - l.log(INFO, format, args...) + l.log(INFO, fmt.Sprintf(format, args...), false) } func (l *Logger) Infoj(j JSON) { - l.log(INFO, "json", j) + l.logJSON(INFO, j) } func (l *Logger) Warn(i ...interface{}) { - l.log(WARN, "", i...) + l.log(WARN, fmt.Sprint(i...), false) } func (l *Logger) Warnf(format string, args ...interface{}) { - l.log(WARN, format, args...) + l.log(WARN, fmt.Sprintf(format, args...), false) } func (l *Logger) Warnj(j JSON) { - l.log(WARN, "json", j) + l.logJSON(WARN, j) } func (l *Logger) Error(i ...interface{}) { - l.log(ERROR, "", i...) + l.log(ERROR, fmt.Sprint(i...), false) } func (l *Logger) Errorf(format string, args ...interface{}) { - l.log(ERROR, format, args...) + l.log(ERROR, fmt.Sprintf(format, args...), false) } func (l *Logger) Errorj(j JSON) { - l.log(ERROR, "json", j) + l.logJSON(ERROR, j) } func (l *Logger) Fatal(i ...interface{}) { - l.log(fatalLevel, "", i...) + l.log(fatalLevel, fmt.Sprint(i...), false) os.Exit(1) } func (l *Logger) Fatalf(format string, args ...interface{}) { - l.log(fatalLevel, format, args...) + l.log(fatalLevel, fmt.Sprintf(format, args...), false) os.Exit(1) } func (l *Logger) Fatalj(j JSON) { - l.log(fatalLevel, "json", j) + l.logJSON(fatalLevel, j) os.Exit(1) } func (l *Logger) Panic(i ...interface{}) { - l.log(panicLevel, "", i...) - panic(fmt.Sprint(i...)) + msg := fmt.Sprint(i...) + l.log(panicLevel, msg, false) + panic(msg) } func (l *Logger) Panicf(format string, args ...interface{}) { - l.log(panicLevel, format, args...) - panic(fmt.Sprintf(format, args...)) + msg := fmt.Sprintf(format, args...) + l.log(panicLevel, msg, false) + panic(msg) } func (l *Logger) Panicj(j JSON) { - l.log(panicLevel, "json", j) + l.logJSON(panicLevel, j) panic(j) } @@ -348,71 +349,76 @@ func Panicj(j JSON) { global.Panicj(j) } -func (l *Logger) log(level Lvl, format string, args ...interface{}) { - if level >= l.Level() || level == 0 { - buf := l.bufferPool.Get().(*bytes.Buffer) - buf.Reset() - defer l.bufferPool.Put(buf) - _, file, line, _ := runtime.Caller(l.skip) - message := "" - - if format == "" { - message = fmt.Sprint(args...) - } else if format == "json" { - b, err := json.Marshal(args[0]) - if err != nil { - panic(err) - } - message = string(b) - } else { - message = fmt.Sprintf(format, args...) +func (l *Logger) logJSON(level Lvl, j JSON) { + b, err := json.Marshal(j) + if err != nil { + panic(err) + } + l.log(level, string(b), true) +} + +func (l *Logger) log(level Lvl, message string, jsonBody bool) { + if level < l.Level() && level != 0 { + return + } + buf := l.bufferPool.Get().(*bytes.Buffer) + buf.Reset() + defer l.bufferPool.Put(buf) + // JSON callers route through an extra logJSON wrapper; account for + // that frame so runtime.Caller still lands on the user's code. Keep + // this in sync with logJSON — if the wrapper is ever inlined away + // or moved, drop the increment. + skip := l.skip + if jsonBody { + skip++ + } + _, file, line, _ := runtime.Caller(skip) + + _, err := l.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { + switch tag { + case "time_rfc3339": + return w.Write([]byte(time.Now().Format(time.RFC3339))) + case "time_rfc3339_nano": + return w.Write([]byte(time.Now().Format(time.RFC3339Nano))) + case "level": + return w.Write([]byte(l.levels[level])) + case "prefix": + return w.Write([]byte(l.prefix)) + case "long_file": + return w.Write([]byte(file)) + case "short_file": + return w.Write([]byte(path.Base(file))) + case "line": + return w.Write([]byte(strconv.Itoa(line))) } + return 0, nil + }) + if err != nil { + return + } - _, err := l.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { - switch tag { - case "time_rfc3339": - return w.Write([]byte(time.Now().Format(time.RFC3339))) - case "time_rfc3339_nano": - return w.Write([]byte(time.Now().Format(time.RFC3339Nano))) - case "level": - return w.Write([]byte(l.levels[level])) - case "prefix": - return w.Write([]byte(l.prefix)) - case "long_file": - return w.Write([]byte(file)) - case "short_file": - return w.Write([]byte(path.Base(file))) - case "line": - return w.Write([]byte(strconv.Itoa(line))) - } - return 0, nil - }) - - if err == nil { - s := buf.String() - i := buf.Len() - 1 - if i >= 0 && s[i] == '}' { - // JSON header - buf.Truncate(i) - buf.WriteByte(',') - if format == "json" { - buf.WriteString(message[1:]) - } else { - buf.WriteString(`"message":`) - buf.WriteString(strconv.Quote(message)) - buf.WriteString(`}`) - } - } else { - // Text header - if len(s) > 0 { - buf.WriteByte(' ') - } - buf.WriteString(message) - } - buf.WriteByte('\n') - l.mutex.Lock() - defer l.mutex.Unlock() - l.output.Write(buf.Bytes()) + s := buf.String() + i := buf.Len() - 1 + if i >= 0 && s[i] == '}' { + // JSON header + buf.Truncate(i) + buf.WriteByte(',') + if jsonBody { + buf.WriteString(message[1:]) + } else { + buf.WriteString(`"message":`) + buf.WriteString(strconv.Quote(message)) + buf.WriteString(`}`) + } + } else { + // Text header + if len(s) > 0 { + buf.WriteByte(' ') } + buf.WriteString(message) } + buf.WriteByte('\n') + l.mutex.Lock() + defer l.mutex.Unlock() + l.output.Write(buf.Bytes()) } diff --git a/vendor/github.com/mailru/easyjson/LICENSE b/vendor/github.com/mailru/easyjson/LICENSE deleted file mode 100644 index fbff658f7..000000000 --- a/vendor/github.com/mailru/easyjson/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2016 Mail.Ru Group - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mailru/easyjson/buffer/pool.go b/vendor/github.com/mailru/easyjson/buffer/pool.go deleted file mode 100644 index 598a54af9..000000000 --- a/vendor/github.com/mailru/easyjson/buffer/pool.go +++ /dev/null @@ -1,278 +0,0 @@ -// Package buffer implements a buffer for serialization, consisting of a chain of []byte-s to -// reduce copying and to allow reuse of individual chunks. -package buffer - -import ( - "io" - "net" - "sync" -) - -// PoolConfig contains configuration for the allocation and reuse strategy. -type PoolConfig struct { - StartSize int // Minimum chunk size that is allocated. - PooledSize int // Minimum chunk size that is reused, reusing chunks too small will result in overhead. - MaxSize int // Maximum chunk size that will be allocated. -} - -var config = PoolConfig{ - StartSize: 128, - PooledSize: 512, - MaxSize: 32768, -} - -// Reuse pool: chunk size -> pool. -var buffers = map[int]*sync.Pool{} - -func initBuffers() { - for l := config.PooledSize; l <= config.MaxSize; l *= 2 { - buffers[l] = new(sync.Pool) - } -} - -func init() { - initBuffers() -} - -// Init sets up a non-default pooling and allocation strategy. Should be run before serialization is done. -func Init(cfg PoolConfig) { - config = cfg - initBuffers() -} - -// putBuf puts a chunk to reuse pool if it can be reused. -func putBuf(buf []byte) { - size := cap(buf) - if size < config.PooledSize { - return - } - if c := buffers[size]; c != nil { - c.Put(buf[:0]) - } -} - -// getBuf gets a chunk from reuse pool or creates a new one if reuse failed. -func getBuf(size int) []byte { - if size >= config.PooledSize { - if c := buffers[size]; c != nil { - v := c.Get() - if v != nil { - return v.([]byte) - } - } - } - return make([]byte, 0, size) -} - -// Buffer is a buffer optimized for serialization without extra copying. -type Buffer struct { - - // Buf is the current chunk that can be used for serialization. - Buf []byte - - toPool []byte - bufs [][]byte -} - -// EnsureSpace makes sure that the current chunk contains at least s free bytes, -// possibly creating a new chunk. -func (b *Buffer) EnsureSpace(s int) { - if cap(b.Buf)-len(b.Buf) < s { - b.ensureSpaceSlow(s) - } -} - -func (b *Buffer) ensureSpaceSlow(s int) { - l := len(b.Buf) - if l > 0 { - if cap(b.toPool) != cap(b.Buf) { - // Chunk was reallocated, toPool can be pooled. - putBuf(b.toPool) - } - if cap(b.bufs) == 0 { - b.bufs = make([][]byte, 0, 8) - } - b.bufs = append(b.bufs, b.Buf) - l = cap(b.toPool) * 2 - } else { - l = config.StartSize - } - - if l > config.MaxSize { - l = config.MaxSize - } - b.Buf = getBuf(l) - b.toPool = b.Buf -} - -// AppendByte appends a single byte to buffer. -func (b *Buffer) AppendByte(data byte) { - b.EnsureSpace(1) - b.Buf = append(b.Buf, data) -} - -// AppendBytes appends a byte slice to buffer. -func (b *Buffer) AppendBytes(data []byte) { - if len(data) <= cap(b.Buf)-len(b.Buf) { - b.Buf = append(b.Buf, data...) // fast path - } else { - b.appendBytesSlow(data) - } -} - -func (b *Buffer) appendBytesSlow(data []byte) { - for len(data) > 0 { - b.EnsureSpace(1) - - sz := cap(b.Buf) - len(b.Buf) - if sz > len(data) { - sz = len(data) - } - - b.Buf = append(b.Buf, data[:sz]...) - data = data[sz:] - } -} - -// AppendString appends a string to buffer. -func (b *Buffer) AppendString(data string) { - if len(data) <= cap(b.Buf)-len(b.Buf) { - b.Buf = append(b.Buf, data...) // fast path - } else { - b.appendStringSlow(data) - } -} - -func (b *Buffer) appendStringSlow(data string) { - for len(data) > 0 { - b.EnsureSpace(1) - - sz := cap(b.Buf) - len(b.Buf) - if sz > len(data) { - sz = len(data) - } - - b.Buf = append(b.Buf, data[:sz]...) - data = data[sz:] - } -} - -// Size computes the size of a buffer by adding sizes of every chunk. -func (b *Buffer) Size() int { - size := len(b.Buf) - for _, buf := range b.bufs { - size += len(buf) - } - return size -} - -// DumpTo outputs the contents of a buffer to a writer and resets the buffer. -func (b *Buffer) DumpTo(w io.Writer) (written int, err error) { - bufs := net.Buffers(b.bufs) - if len(b.Buf) > 0 { - bufs = append(bufs, b.Buf) - } - n, err := bufs.WriteTo(w) - - for _, buf := range b.bufs { - putBuf(buf) - } - putBuf(b.toPool) - - b.bufs = nil - b.Buf = nil - b.toPool = nil - - return int(n), err -} - -// BuildBytes creates a single byte slice with all the contents of the buffer. Data is -// copied if it does not fit in a single chunk. You can optionally provide one byte -// slice as argument that it will try to reuse. -func (b *Buffer) BuildBytes(reuse ...[]byte) []byte { - if len(b.bufs) == 0 { - ret := b.Buf - b.toPool = nil - b.Buf = nil - return ret - } - - var ret []byte - size := b.Size() - - // If we got a buffer as argument and it is big enough, reuse it. - if len(reuse) == 1 && cap(reuse[0]) >= size { - ret = reuse[0][:0] - } else { - ret = make([]byte, 0, size) - } - for _, buf := range b.bufs { - ret = append(ret, buf...) - putBuf(buf) - } - - ret = append(ret, b.Buf...) - putBuf(b.toPool) - - b.bufs = nil - b.toPool = nil - b.Buf = nil - - return ret -} - -type readCloser struct { - offset int - bufs [][]byte -} - -func (r *readCloser) Read(p []byte) (n int, err error) { - for _, buf := range r.bufs { - // Copy as much as we can. - x := copy(p[n:], buf[r.offset:]) - n += x // Increment how much we filled. - - // Did we empty the whole buffer? - if r.offset+x == len(buf) { - // On to the next buffer. - r.offset = 0 - r.bufs = r.bufs[1:] - - // We can release this buffer. - putBuf(buf) - } else { - r.offset += x - } - - if n == len(p) { - break - } - } - // No buffers left or nothing read? - if len(r.bufs) == 0 { - err = io.EOF - } - return -} - -func (r *readCloser) Close() error { - // Release all remaining buffers. - for _, buf := range r.bufs { - putBuf(buf) - } - // In case Close gets called multiple times. - r.bufs = nil - - return nil -} - -// ReadCloser creates an io.ReadCloser with all the contents of the buffer. -func (b *Buffer) ReadCloser() io.ReadCloser { - ret := &readCloser{0, append(b.bufs, b.Buf)} - - b.bufs = nil - b.toPool = nil - b.Buf = nil - - return ret -} diff --git a/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go b/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go deleted file mode 100644 index ff7b27c5b..000000000 --- a/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go +++ /dev/null @@ -1,24 +0,0 @@ -// This file will only be included to the build if neither -// easyjson_nounsafe nor appengine build tag is set. See README notes -// for more details. - -//+build !easyjson_nounsafe -//+build !appengine - -package jlexer - -import ( - "reflect" - "unsafe" -) - -// bytesToStr creates a string pointing at the slice to avoid copying. -// -// Warning: the string returned by the function should be used with care, as the whole input data -// chunk may be either blocked from being freed by GC because of a single string or the buffer.Data -// may be garbage-collected even when the string exists. -func bytesToStr(data []byte) string { - h := (*reflect.SliceHeader)(unsafe.Pointer(&data)) - shdr := reflect.StringHeader{Data: h.Data, Len: h.Len} - return *(*string)(unsafe.Pointer(&shdr)) -} diff --git a/vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go b/vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go deleted file mode 100644 index 864d1be67..000000000 --- a/vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go +++ /dev/null @@ -1,13 +0,0 @@ -// This file is included to the build if any of the buildtags below -// are defined. Refer to README notes for more details. - -//+build easyjson_nounsafe appengine - -package jlexer - -// bytesToStr creates a string normally from []byte -// -// Note that this method is roughly 1.5x slower than using the 'unsafe' method. -func bytesToStr(data []byte) string { - return string(data) -} diff --git a/vendor/github.com/mailru/easyjson/jlexer/error.go b/vendor/github.com/mailru/easyjson/jlexer/error.go deleted file mode 100644 index e90ec40d0..000000000 --- a/vendor/github.com/mailru/easyjson/jlexer/error.go +++ /dev/null @@ -1,15 +0,0 @@ -package jlexer - -import "fmt" - -// LexerError implements the error interface and represents all possible errors that can be -// generated during parsing the JSON data. -type LexerError struct { - Reason string - Offset int - Data string -} - -func (l *LexerError) Error() string { - return fmt.Sprintf("parse error: %s near offset %d of '%s'", l.Reason, l.Offset, l.Data) -} diff --git a/vendor/github.com/mailru/easyjson/jlexer/lexer.go b/vendor/github.com/mailru/easyjson/jlexer/lexer.go deleted file mode 100644 index b5f5e2613..000000000 --- a/vendor/github.com/mailru/easyjson/jlexer/lexer.go +++ /dev/null @@ -1,1244 +0,0 @@ -// Package jlexer contains a JSON lexer implementation. -// -// It is expected that it is mostly used with generated parser code, so the interface is tuned -// for a parser that knows what kind of data is expected. -package jlexer - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io" - "strconv" - "unicode" - "unicode/utf16" - "unicode/utf8" - - "github.com/josharian/intern" -) - -// tokenKind determines type of a token. -type tokenKind byte - -const ( - tokenUndef tokenKind = iota // No token. - tokenDelim // Delimiter: one of '{', '}', '[' or ']'. - tokenString // A string literal, e.g. "abc\u1234" - tokenNumber // Number literal, e.g. 1.5e5 - tokenBool // Boolean literal: true or false. - tokenNull // null keyword. -) - -// token describes a single token: type, position in the input and value. -type token struct { - kind tokenKind // Type of a token. - - boolValue bool // Value if a boolean literal token. - byteValueCloned bool // true if byteValue was allocated and does not refer to original json body - byteValue []byte // Raw value of a token. - delimValue byte -} - -// Lexer is a JSON lexer: it iterates over JSON tokens in a byte slice. -type Lexer struct { - Data []byte // Input data given to the lexer. - - start int // Start of the current token. - pos int // Current unscanned position in the input stream. - token token // Last scanned token, if token.kind != tokenUndef. - - firstElement bool // Whether current element is the first in array or an object. - wantSep byte // A comma or a colon character, which need to occur before a token. - - UseMultipleErrors bool // If we want to use multiple errors. - fatalError error // Fatal error occurred during lexing. It is usually a syntax error. - multipleErrors []*LexerError // Semantic errors occurred during lexing. Marshalling will be continued after finding this errors. -} - -// FetchToken scans the input for the next token. -func (r *Lexer) FetchToken() { - r.token.kind = tokenUndef - r.start = r.pos - - // Check if r.Data has r.pos element - // If it doesn't, it mean corrupted input data - if len(r.Data) < r.pos { - r.errParse("Unexpected end of data") - return - } - // Determine the type of a token by skipping whitespace and reading the - // first character. - for _, c := range r.Data[r.pos:] { - switch c { - case ':', ',': - if r.wantSep == c { - r.pos++ - r.start++ - r.wantSep = 0 - } else { - r.errSyntax() - } - - case ' ', '\t', '\r', '\n': - r.pos++ - r.start++ - - case '"': - if r.wantSep != 0 { - r.errSyntax() - } - - r.token.kind = tokenString - r.fetchString() - return - - case '{', '[': - if r.wantSep != 0 { - r.errSyntax() - } - r.firstElement = true - r.token.kind = tokenDelim - r.token.delimValue = r.Data[r.pos] - r.pos++ - return - - case '}', ']': - if !r.firstElement && (r.wantSep != ',') { - r.errSyntax() - } - r.wantSep = 0 - r.token.kind = tokenDelim - r.token.delimValue = r.Data[r.pos] - r.pos++ - return - - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': - if r.wantSep != 0 { - r.errSyntax() - } - r.token.kind = tokenNumber - r.fetchNumber() - return - - case 'n': - if r.wantSep != 0 { - r.errSyntax() - } - - r.token.kind = tokenNull - r.fetchNull() - return - - case 't': - if r.wantSep != 0 { - r.errSyntax() - } - - r.token.kind = tokenBool - r.token.boolValue = true - r.fetchTrue() - return - - case 'f': - if r.wantSep != 0 { - r.errSyntax() - } - - r.token.kind = tokenBool - r.token.boolValue = false - r.fetchFalse() - return - - default: - r.errSyntax() - return - } - } - r.fatalError = io.EOF - return -} - -// isTokenEnd returns true if the char can follow a non-delimiter token -func isTokenEnd(c byte) bool { - return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '[' || c == ']' || c == '{' || c == '}' || c == ',' || c == ':' -} - -// fetchNull fetches and checks remaining bytes of null keyword. -func (r *Lexer) fetchNull() { - r.pos += 4 - if r.pos > len(r.Data) || - r.Data[r.pos-3] != 'u' || - r.Data[r.pos-2] != 'l' || - r.Data[r.pos-1] != 'l' || - (r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) { - - r.pos -= 4 - r.errSyntax() - } -} - -// fetchTrue fetches and checks remaining bytes of true keyword. -func (r *Lexer) fetchTrue() { - r.pos += 4 - if r.pos > len(r.Data) || - r.Data[r.pos-3] != 'r' || - r.Data[r.pos-2] != 'u' || - r.Data[r.pos-1] != 'e' || - (r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) { - - r.pos -= 4 - r.errSyntax() - } -} - -// fetchFalse fetches and checks remaining bytes of false keyword. -func (r *Lexer) fetchFalse() { - r.pos += 5 - if r.pos > len(r.Data) || - r.Data[r.pos-4] != 'a' || - r.Data[r.pos-3] != 'l' || - r.Data[r.pos-2] != 's' || - r.Data[r.pos-1] != 'e' || - (r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) { - - r.pos -= 5 - r.errSyntax() - } -} - -// fetchNumber scans a number literal token. -func (r *Lexer) fetchNumber() { - hasE := false - afterE := false - hasDot := false - - r.pos++ - for i, c := range r.Data[r.pos:] { - switch { - case c >= '0' && c <= '9': - afterE = false - case c == '.' && !hasDot: - hasDot = true - case (c == 'e' || c == 'E') && !hasE: - hasE = true - hasDot = true - afterE = true - case (c == '+' || c == '-') && afterE: - afterE = false - default: - r.pos += i - if !isTokenEnd(c) { - r.errSyntax() - } else { - r.token.byteValue = r.Data[r.start:r.pos] - } - return - } - } - - r.pos = len(r.Data) - r.token.byteValue = r.Data[r.start:] -} - -// findStringLen tries to scan into the string literal for ending quote char to determine required size. -// The size will be exact if no escapes are present and may be inexact if there are escaped chars. -func findStringLen(data []byte) (isValid bool, length int) { - for { - idx := bytes.IndexByte(data, '"') - if idx == -1 { - return false, len(data) - } - if idx == 0 || (idx > 0 && data[idx-1] != '\\') { - return true, length + idx - } - - // count \\\\\\\ sequences. even number of slashes means quote is not really escaped - cnt := 1 - for idx-cnt-1 >= 0 && data[idx-cnt-1] == '\\' { - cnt++ - } - if cnt%2 == 0 { - return true, length + idx - } - - length += idx + 1 - data = data[idx+1:] - } -} - -// unescapeStringToken performs unescaping of string token. -// if no escaping is needed, original string is returned, otherwise - a new one allocated -func (r *Lexer) unescapeStringToken() (err error) { - data := r.token.byteValue - var unescapedData []byte - - for { - i := bytes.IndexByte(data, '\\') - if i == -1 { - break - } - - escapedRune, escapedBytes, err := decodeEscape(data[i:]) - if err != nil { - r.errParse(err.Error()) - return err - } - - if unescapedData == nil { - unescapedData = make([]byte, 0, len(r.token.byteValue)) - } - - var d [4]byte - s := utf8.EncodeRune(d[:], escapedRune) - unescapedData = append(unescapedData, data[:i]...) - unescapedData = append(unescapedData, d[:s]...) - - data = data[i+escapedBytes:] - } - - if unescapedData != nil { - r.token.byteValue = append(unescapedData, data...) - r.token.byteValueCloned = true - } - return -} - -// getu4 decodes \uXXXX from the beginning of s, returning the hex value, -// or it returns -1. -func getu4(s []byte) rune { - if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { - return -1 - } - var val rune - for i := 2; i < len(s) && i < 6; i++ { - var v byte - c := s[i] - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - v = c - '0' - case 'a', 'b', 'c', 'd', 'e', 'f': - v = c - 'a' + 10 - case 'A', 'B', 'C', 'D', 'E', 'F': - v = c - 'A' + 10 - default: - return -1 - } - - val <<= 4 - val |= rune(v) - } - return val -} - -// decodeEscape processes a single escape sequence and returns number of bytes processed. -func decodeEscape(data []byte) (decoded rune, bytesProcessed int, err error) { - if len(data) < 2 { - return 0, 0, errors.New("incorrect escape symbol \\ at the end of token") - } - - c := data[1] - switch c { - case '"', '/', '\\': - return rune(c), 2, nil - case 'b': - return '\b', 2, nil - case 'f': - return '\f', 2, nil - case 'n': - return '\n', 2, nil - case 'r': - return '\r', 2, nil - case 't': - return '\t', 2, nil - case 'u': - rr := getu4(data) - if rr < 0 { - return 0, 0, errors.New("incorrectly escaped \\uXXXX sequence") - } - - read := 6 - if utf16.IsSurrogate(rr) { - rr1 := getu4(data[read:]) - if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { - read += 6 - rr = dec - } else { - rr = unicode.ReplacementChar - } - } - return rr, read, nil - } - - return 0, 0, errors.New("incorrectly escaped bytes") -} - -// fetchString scans a string literal token. -func (r *Lexer) fetchString() { - r.pos++ - data := r.Data[r.pos:] - - isValid, length := findStringLen(data) - if !isValid { - r.pos += length - r.errParse("unterminated string literal") - return - } - r.token.byteValue = data[:length] - r.pos += length + 1 // skip closing '"' as well -} - -// scanToken scans the next token if no token is currently available in the lexer. -func (r *Lexer) scanToken() { - if r.token.kind != tokenUndef || r.fatalError != nil { - return - } - - r.FetchToken() -} - -// consume resets the current token to allow scanning the next one. -func (r *Lexer) consume() { - r.token.kind = tokenUndef - r.token.byteValueCloned = false - r.token.delimValue = 0 -} - -// Ok returns true if no error (including io.EOF) was encountered during scanning. -func (r *Lexer) Ok() bool { - return r.fatalError == nil -} - -const maxErrorContextLen = 13 - -func (r *Lexer) errParse(what string) { - if r.fatalError == nil { - var str string - if len(r.Data)-r.pos <= maxErrorContextLen { - str = string(r.Data) - } else { - str = string(r.Data[r.pos:r.pos+maxErrorContextLen-3]) + "..." - } - r.fatalError = &LexerError{ - Reason: what, - Offset: r.pos, - Data: str, - } - } -} - -func (r *Lexer) errSyntax() { - r.errParse("syntax error") -} - -func (r *Lexer) errInvalidToken(expected string) { - if r.fatalError != nil { - return - } - if r.UseMultipleErrors { - r.pos = r.start - r.consume() - r.SkipRecursive() - switch expected { - case "[": - r.token.delimValue = ']' - r.token.kind = tokenDelim - case "{": - r.token.delimValue = '}' - r.token.kind = tokenDelim - } - r.addNonfatalError(&LexerError{ - Reason: fmt.Sprintf("expected %s", expected), - Offset: r.start, - Data: string(r.Data[r.start:r.pos]), - }) - return - } - - var str string - if len(r.token.byteValue) <= maxErrorContextLen { - str = string(r.token.byteValue) - } else { - str = string(r.token.byteValue[:maxErrorContextLen-3]) + "..." - } - r.fatalError = &LexerError{ - Reason: fmt.Sprintf("expected %s", expected), - Offset: r.pos, - Data: str, - } -} - -func (r *Lexer) GetPos() int { - return r.pos -} - -// Delim consumes a token and verifies that it is the given delimiter. -func (r *Lexer) Delim(c byte) { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - - if !r.Ok() || r.token.delimValue != c { - r.consume() // errInvalidToken can change token if UseMultipleErrors is enabled. - r.errInvalidToken(string([]byte{c})) - } else { - r.consume() - } -} - -// IsDelim returns true if there was no scanning error and next token is the given delimiter. -func (r *Lexer) IsDelim(c byte) bool { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - return !r.Ok() || r.token.delimValue == c -} - -// Null verifies that the next token is null and consumes it. -func (r *Lexer) Null() { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - if !r.Ok() || r.token.kind != tokenNull { - r.errInvalidToken("null") - } - r.consume() -} - -// IsNull returns true if the next token is a null keyword. -func (r *Lexer) IsNull() bool { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - return r.Ok() && r.token.kind == tokenNull -} - -// Skip skips a single token. -func (r *Lexer) Skip() { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - r.consume() -} - -// SkipRecursive skips next array or object completely, or just skips a single token if not -// an array/object. -// -// Note: no syntax validation is performed on the skipped data. -func (r *Lexer) SkipRecursive() { - r.scanToken() - var start, end byte - startPos := r.start - - switch r.token.delimValue { - case '{': - start, end = '{', '}' - case '[': - start, end = '[', ']' - default: - r.consume() - return - } - - r.consume() - - level := 1 - inQuotes := false - wasEscape := false - - for i, c := range r.Data[r.pos:] { - switch { - case c == start && !inQuotes: - level++ - case c == end && !inQuotes: - level-- - if level == 0 { - r.pos += i + 1 - if !json.Valid(r.Data[startPos:r.pos]) { - r.pos = len(r.Data) - r.fatalError = &LexerError{ - Reason: "skipped array/object json value is invalid", - Offset: r.pos, - Data: string(r.Data[r.pos:]), - } - } - return - } - case c == '\\' && inQuotes: - wasEscape = !wasEscape - continue - case c == '"' && inQuotes: - inQuotes = wasEscape - case c == '"': - inQuotes = true - } - wasEscape = false - } - r.pos = len(r.Data) - r.fatalError = &LexerError{ - Reason: "EOF reached while skipping array/object or token", - Offset: r.pos, - Data: string(r.Data[r.pos:]), - } -} - -// Raw fetches the next item recursively as a data slice -func (r *Lexer) Raw() []byte { - r.SkipRecursive() - if !r.Ok() { - return nil - } - return r.Data[r.start:r.pos] -} - -// IsStart returns whether the lexer is positioned at the start -// of an input string. -func (r *Lexer) IsStart() bool { - return r.pos == 0 -} - -// Consumed reads all remaining bytes from the input, publishing an error if -// there is anything but whitespace remaining. -func (r *Lexer) Consumed() { - if r.pos > len(r.Data) || !r.Ok() { - return - } - - for _, c := range r.Data[r.pos:] { - if c != ' ' && c != '\t' && c != '\r' && c != '\n' { - r.AddError(&LexerError{ - Reason: "invalid character '" + string(c) + "' after top-level value", - Offset: r.pos, - Data: string(r.Data[r.pos:]), - }) - return - } - - r.pos++ - r.start++ - } -} - -func (r *Lexer) unsafeString(skipUnescape bool) (string, []byte) { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - if !r.Ok() || r.token.kind != tokenString { - r.errInvalidToken("string") - return "", nil - } - if !skipUnescape { - if err := r.unescapeStringToken(); err != nil { - r.errInvalidToken("string") - return "", nil - } - } - - bytes := r.token.byteValue - ret := bytesToStr(r.token.byteValue) - r.consume() - return ret, bytes -} - -// UnsafeString returns the string value if the token is a string literal. -// -// Warning: returned string may point to the input buffer, so the string should not outlive -// the input buffer. Intended pattern of usage is as an argument to a switch statement. -func (r *Lexer) UnsafeString() string { - ret, _ := r.unsafeString(false) - return ret -} - -// UnsafeBytes returns the byte slice if the token is a string literal. -func (r *Lexer) UnsafeBytes() []byte { - _, ret := r.unsafeString(false) - return ret -} - -// UnsafeFieldName returns current member name string token -func (r *Lexer) UnsafeFieldName(skipUnescape bool) string { - ret, _ := r.unsafeString(skipUnescape) - return ret -} - -// String reads a string literal. -func (r *Lexer) String() string { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - if !r.Ok() || r.token.kind != tokenString { - r.errInvalidToken("string") - return "" - } - if err := r.unescapeStringToken(); err != nil { - r.errInvalidToken("string") - return "" - } - var ret string - if r.token.byteValueCloned { - ret = bytesToStr(r.token.byteValue) - } else { - ret = string(r.token.byteValue) - } - r.consume() - return ret -} - -// StringIntern reads a string literal, and performs string interning on it. -func (r *Lexer) StringIntern() string { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - if !r.Ok() || r.token.kind != tokenString { - r.errInvalidToken("string") - return "" - } - if err := r.unescapeStringToken(); err != nil { - r.errInvalidToken("string") - return "" - } - ret := intern.Bytes(r.token.byteValue) - r.consume() - return ret -} - -// Bytes reads a string literal and base64 decodes it into a byte slice. -func (r *Lexer) Bytes() []byte { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - if !r.Ok() || r.token.kind != tokenString { - r.errInvalidToken("string") - return nil - } - if err := r.unescapeStringToken(); err != nil { - r.errInvalidToken("string") - return nil - } - ret := make([]byte, base64.StdEncoding.DecodedLen(len(r.token.byteValue))) - n, err := base64.StdEncoding.Decode(ret, r.token.byteValue) - if err != nil { - r.fatalError = &LexerError{ - Reason: err.Error(), - } - return nil - } - - r.consume() - return ret[:n] -} - -// Bool reads a true or false boolean keyword. -func (r *Lexer) Bool() bool { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - if !r.Ok() || r.token.kind != tokenBool { - r.errInvalidToken("bool") - return false - } - ret := r.token.boolValue - r.consume() - return ret -} - -func (r *Lexer) number() string { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - if !r.Ok() || r.token.kind != tokenNumber { - r.errInvalidToken("number") - return "" - } - ret := bytesToStr(r.token.byteValue) - r.consume() - return ret -} - -func (r *Lexer) Uint8() uint8 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseUint(s, 10, 8) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return uint8(n) -} - -func (r *Lexer) Uint16() uint16 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseUint(s, 10, 16) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return uint16(n) -} - -func (r *Lexer) Uint32() uint32 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseUint(s, 10, 32) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return uint32(n) -} - -func (r *Lexer) Uint64() uint64 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseUint(s, 10, 64) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return n -} - -func (r *Lexer) Uint() uint { - return uint(r.Uint64()) -} - -func (r *Lexer) Int8() int8 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseInt(s, 10, 8) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return int8(n) -} - -func (r *Lexer) Int16() int16 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseInt(s, 10, 16) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return int16(n) -} - -func (r *Lexer) Int32() int32 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseInt(s, 10, 32) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return int32(n) -} - -func (r *Lexer) Int64() int64 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseInt(s, 10, 64) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return n -} - -func (r *Lexer) Int() int { - return int(r.Int64()) -} - -func (r *Lexer) Uint8Str() uint8 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseUint(s, 10, 8) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return uint8(n) -} - -func (r *Lexer) Uint16Str() uint16 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseUint(s, 10, 16) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return uint16(n) -} - -func (r *Lexer) Uint32Str() uint32 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseUint(s, 10, 32) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return uint32(n) -} - -func (r *Lexer) Uint64Str() uint64 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseUint(s, 10, 64) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return n -} - -func (r *Lexer) UintStr() uint { - return uint(r.Uint64Str()) -} - -func (r *Lexer) UintptrStr() uintptr { - return uintptr(r.Uint64Str()) -} - -func (r *Lexer) Int8Str() int8 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseInt(s, 10, 8) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return int8(n) -} - -func (r *Lexer) Int16Str() int16 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseInt(s, 10, 16) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return int16(n) -} - -func (r *Lexer) Int32Str() int32 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseInt(s, 10, 32) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return int32(n) -} - -func (r *Lexer) Int64Str() int64 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseInt(s, 10, 64) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return n -} - -func (r *Lexer) IntStr() int { - return int(r.Int64Str()) -} - -func (r *Lexer) Float32() float32 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseFloat(s, 32) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return float32(n) -} - -func (r *Lexer) Float32Str() float32 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - n, err := strconv.ParseFloat(s, 32) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return float32(n) -} - -func (r *Lexer) Float64() float64 { - s := r.number() - if !r.Ok() { - return 0 - } - - n, err := strconv.ParseFloat(s, 64) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: s, - }) - } - return n -} - -func (r *Lexer) Float64Str() float64 { - s, b := r.unsafeString(false) - if !r.Ok() { - return 0 - } - n, err := strconv.ParseFloat(s, 64) - if err != nil { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Reason: err.Error(), - Data: string(b), - }) - } - return n -} - -func (r *Lexer) Error() error { - return r.fatalError -} - -func (r *Lexer) AddError(e error) { - if r.fatalError == nil { - r.fatalError = e - } -} - -func (r *Lexer) AddNonFatalError(e error) { - r.addNonfatalError(&LexerError{ - Offset: r.start, - Data: string(r.Data[r.start:r.pos]), - Reason: e.Error(), - }) -} - -func (r *Lexer) addNonfatalError(err *LexerError) { - if r.UseMultipleErrors { - // We don't want to add errors with the same offset. - if len(r.multipleErrors) != 0 && r.multipleErrors[len(r.multipleErrors)-1].Offset == err.Offset { - return - } - r.multipleErrors = append(r.multipleErrors, err) - return - } - r.fatalError = err -} - -func (r *Lexer) GetNonFatalErrors() []*LexerError { - return r.multipleErrors -} - -// JsonNumber fetches and json.Number from 'encoding/json' package. -// Both int, float or string, contains them are valid values -func (r *Lexer) JsonNumber() json.Number { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - if !r.Ok() { - r.errInvalidToken("json.Number") - return json.Number("") - } - - switch r.token.kind { - case tokenString: - return json.Number(r.String()) - case tokenNumber: - return json.Number(r.Raw()) - case tokenNull: - r.Null() - return json.Number("") - default: - r.errSyntax() - return json.Number("") - } -} - -// Interface fetches an interface{} analogous to the 'encoding/json' package. -func (r *Lexer) Interface() interface{} { - if r.token.kind == tokenUndef && r.Ok() { - r.FetchToken() - } - - if !r.Ok() { - return nil - } - switch r.token.kind { - case tokenString: - return r.String() - case tokenNumber: - return r.Float64() - case tokenBool: - return r.Bool() - case tokenNull: - r.Null() - return nil - } - - if r.token.delimValue == '{' { - r.consume() - - ret := map[string]interface{}{} - for !r.IsDelim('}') { - key := r.String() - r.WantColon() - ret[key] = r.Interface() - r.WantComma() - } - r.Delim('}') - - if r.Ok() { - return ret - } else { - return nil - } - } else if r.token.delimValue == '[' { - r.consume() - - ret := []interface{}{} - for !r.IsDelim(']') { - ret = append(ret, r.Interface()) - r.WantComma() - } - r.Delim(']') - - if r.Ok() { - return ret - } else { - return nil - } - } - r.errSyntax() - return nil -} - -// WantComma requires a comma to be present before fetching next token. -func (r *Lexer) WantComma() { - r.wantSep = ',' - r.firstElement = false -} - -// WantColon requires a colon to be present before fetching next token. -func (r *Lexer) WantColon() { - r.wantSep = ':' - r.firstElement = false -} diff --git a/vendor/github.com/mailru/easyjson/jwriter/writer.go b/vendor/github.com/mailru/easyjson/jwriter/writer.go deleted file mode 100644 index 2c5b20105..000000000 --- a/vendor/github.com/mailru/easyjson/jwriter/writer.go +++ /dev/null @@ -1,405 +0,0 @@ -// Package jwriter contains a JSON writer. -package jwriter - -import ( - "io" - "strconv" - "unicode/utf8" - - "github.com/mailru/easyjson/buffer" -) - -// Flags describe various encoding options. The behavior may be actually implemented in the encoder, but -// Flags field in Writer is used to set and pass them around. -type Flags int - -const ( - NilMapAsEmpty Flags = 1 << iota // Encode nil map as '{}' rather than 'null'. - NilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'. -) - -// Writer is a JSON writer. -type Writer struct { - Flags Flags - - Error error - Buffer buffer.Buffer - NoEscapeHTML bool -} - -// Size returns the size of the data that was written out. -func (w *Writer) Size() int { - return w.Buffer.Size() -} - -// DumpTo outputs the data to given io.Writer, resetting the buffer. -func (w *Writer) DumpTo(out io.Writer) (written int, err error) { - return w.Buffer.DumpTo(out) -} - -// BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice -// as argument that it will try to reuse. -func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) { - if w.Error != nil { - return nil, w.Error - } - - return w.Buffer.BuildBytes(reuse...), nil -} - -// ReadCloser returns an io.ReadCloser that can be used to read the data. -// ReadCloser also resets the buffer. -func (w *Writer) ReadCloser() (io.ReadCloser, error) { - if w.Error != nil { - return nil, w.Error - } - - return w.Buffer.ReadCloser(), nil -} - -// RawByte appends raw binary data to the buffer. -func (w *Writer) RawByte(c byte) { - w.Buffer.AppendByte(c) -} - -// RawByte appends raw binary data to the buffer. -func (w *Writer) RawString(s string) { - w.Buffer.AppendString(s) -} - -// Raw appends raw binary data to the buffer or sets the error if it is given. Useful for -// calling with results of MarshalJSON-like functions. -func (w *Writer) Raw(data []byte, err error) { - switch { - case w.Error != nil: - return - case err != nil: - w.Error = err - case len(data) > 0: - w.Buffer.AppendBytes(data) - default: - w.RawString("null") - } -} - -// RawText encloses raw binary data in quotes and appends in to the buffer. -// Useful for calling with results of MarshalText-like functions. -func (w *Writer) RawText(data []byte, err error) { - switch { - case w.Error != nil: - return - case err != nil: - w.Error = err - case len(data) > 0: - w.String(string(data)) - default: - w.RawString("null") - } -} - -// Base64Bytes appends data to the buffer after base64 encoding it -func (w *Writer) Base64Bytes(data []byte) { - if data == nil { - w.Buffer.AppendString("null") - return - } - w.Buffer.AppendByte('"') - w.base64(data) - w.Buffer.AppendByte('"') -} - -func (w *Writer) Uint8(n uint8) { - w.Buffer.EnsureSpace(3) - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) -} - -func (w *Writer) Uint16(n uint16) { - w.Buffer.EnsureSpace(5) - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) -} - -func (w *Writer) Uint32(n uint32) { - w.Buffer.EnsureSpace(10) - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) -} - -func (w *Writer) Uint(n uint) { - w.Buffer.EnsureSpace(20) - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) -} - -func (w *Writer) Uint64(n uint64) { - w.Buffer.EnsureSpace(20) - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) -} - -func (w *Writer) Int8(n int8) { - w.Buffer.EnsureSpace(4) - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) -} - -func (w *Writer) Int16(n int16) { - w.Buffer.EnsureSpace(6) - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) -} - -func (w *Writer) Int32(n int32) { - w.Buffer.EnsureSpace(11) - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) -} - -func (w *Writer) Int(n int) { - w.Buffer.EnsureSpace(21) - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) -} - -func (w *Writer) Int64(n int64) { - w.Buffer.EnsureSpace(21) - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) -} - -func (w *Writer) Uint8Str(n uint8) { - w.Buffer.EnsureSpace(3) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Uint16Str(n uint16) { - w.Buffer.EnsureSpace(5) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Uint32Str(n uint32) { - w.Buffer.EnsureSpace(10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) UintStr(n uint) { - w.Buffer.EnsureSpace(20) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Uint64Str(n uint64) { - w.Buffer.EnsureSpace(20) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) UintptrStr(n uintptr) { - w.Buffer.EnsureSpace(20) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Int8Str(n int8) { - w.Buffer.EnsureSpace(4) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Int16Str(n int16) { - w.Buffer.EnsureSpace(6) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Int32Str(n int32) { - w.Buffer.EnsureSpace(11) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) IntStr(n int) { - w.Buffer.EnsureSpace(21) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Int64Str(n int64) { - w.Buffer.EnsureSpace(21) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Float32(n float32) { - w.Buffer.EnsureSpace(20) - w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) -} - -func (w *Writer) Float32Str(n float32) { - w.Buffer.EnsureSpace(20) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Float64(n float64) { - w.Buffer.EnsureSpace(20) - w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64) -} - -func (w *Writer) Float64Str(n float64) { - w.Buffer.EnsureSpace(20) - w.Buffer.Buf = append(w.Buffer.Buf, '"') - w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64) - w.Buffer.Buf = append(w.Buffer.Buf, '"') -} - -func (w *Writer) Bool(v bool) { - w.Buffer.EnsureSpace(5) - if v { - w.Buffer.Buf = append(w.Buffer.Buf, "true"...) - } else { - w.Buffer.Buf = append(w.Buffer.Buf, "false"...) - } -} - -const chars = "0123456789abcdef" - -func getTable(falseValues ...int) [128]bool { - table := [128]bool{} - - for i := 0; i < 128; i++ { - table[i] = true - } - - for _, v := range falseValues { - table[v] = false - } - - return table -} - -var ( - htmlEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '&', '<', '>', '\\') - htmlNoEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '\\') -) - -func (w *Writer) String(s string) { - w.Buffer.AppendByte('"') - - // Portions of the string that contain no escapes are appended as - // byte slices. - - p := 0 // last non-escape symbol - - escapeTable := &htmlEscapeTable - if w.NoEscapeHTML { - escapeTable = &htmlNoEscapeTable - } - - for i := 0; i < len(s); { - c := s[i] - - if c < utf8.RuneSelf { - if escapeTable[c] { - // single-width character, no escaping is required - i++ - continue - } - - w.Buffer.AppendString(s[p:i]) - switch c { - case '\t': - w.Buffer.AppendString(`\t`) - case '\r': - w.Buffer.AppendString(`\r`) - case '\n': - w.Buffer.AppendString(`\n`) - case '\\': - w.Buffer.AppendString(`\\`) - case '"': - w.Buffer.AppendString(`\"`) - default: - w.Buffer.AppendString(`\u00`) - w.Buffer.AppendByte(chars[c>>4]) - w.Buffer.AppendByte(chars[c&0xf]) - } - - i++ - p = i - continue - } - - // broken utf - runeValue, runeWidth := utf8.DecodeRuneInString(s[i:]) - if runeValue == utf8.RuneError && runeWidth == 1 { - w.Buffer.AppendString(s[p:i]) - w.Buffer.AppendString(`\ufffd`) - i++ - p = i - continue - } - - // jsonp stuff - tab separator and line separator - if runeValue == '\u2028' || runeValue == '\u2029' { - w.Buffer.AppendString(s[p:i]) - w.Buffer.AppendString(`\u202`) - w.Buffer.AppendByte(chars[runeValue&0xf]) - i += runeWidth - p = i - continue - } - i += runeWidth - } - w.Buffer.AppendString(s[p:]) - w.Buffer.AppendByte('"') -} - -const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -const padChar = '=' - -func (w *Writer) base64(in []byte) { - - if len(in) == 0 { - return - } - - w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4) - - si := 0 - n := (len(in) / 3) * 3 - - for si < n { - // Convert 3x 8bit source bytes into 4 bytes - val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2]) - - w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F], encode[val>>6&0x3F], encode[val&0x3F]) - - si += 3 - } - - remain := len(in) - si - if remain == 0 { - return - } - - // Add the remaining small block - val := uint(in[si+0]) << 16 - if remain == 2 { - val |= uint(in[si+1]) << 8 - } - - w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F]) - - switch remain { - case 2: - w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>6&0x3F], byte(padChar)) - case 1: - w.Buffer.Buf = append(w.Buffer.Buf, byte(padChar), byte(padChar)) - } -} diff --git a/vendor/github.com/mattn/go-colorable/colorable_appengine.go b/vendor/github.com/mattn/go-colorable/colorable_appengine.go deleted file mode 100644 index 416d1bbbf..000000000 --- a/vendor/github.com/mattn/go-colorable/colorable_appengine.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build appengine -// +build appengine - -package colorable - -import ( - "io" - "os" - - _ "github.com/mattn/go-isatty" -) - -// NewColorable returns new instance of Writer which handles escape sequence. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return os.Stdout -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return os.Stderr -} - -// EnableColorsStdout enable colors if possible. -func EnableColorsStdout(enabled *bool) func() { - if enabled != nil { - *enabled = true - } - return func() {} -} diff --git a/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/mattn/go-colorable/colorable_others.go index 766d94603..c1a78aa94 100644 --- a/vendor/github.com/mattn/go-colorable/colorable_others.go +++ b/vendor/github.com/mattn/go-colorable/colorable_others.go @@ -1,5 +1,5 @@ -//go:build !windows && !appengine -// +build !windows,!appengine +//go:build !windows || appengine +// +build !windows appengine package colorable diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go index 1846ad5ab..426a409ca 100644 --- a/vendor/github.com/mattn/go-colorable/colorable_windows.go +++ b/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -5,13 +5,13 @@ package colorable import ( "bytes" + syscall "golang.org/x/sys/windows" "io" "math" "os" "strconv" "strings" "sync" - "syscall" "unsafe" "github.com/mattn/go-isatty" @@ -73,7 +73,7 @@ type consoleCursorInfo struct { } var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") + kernel32 = syscall.NewLazySystemDLL("kernel32.dll") procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") @@ -87,18 +87,19 @@ var ( procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") ) -// Writer provides colorable Writer to the console -type Writer struct { +// writer provides colorable Writer to the console +type writer struct { out io.Writer handle syscall.Handle althandle syscall.Handle oldattr word + curattr word oldpos coord rest bytes.Buffer mutex sync.Mutex } -// NewColorable returns new instance of Writer which handles escape sequence from File. +// NewColorable returns new instance of writer which handles escape sequence from File. func NewColorable(file *os.File) io.Writer { if file == nil { panic("nil passed instead of *os.File to NewColorable()") @@ -112,17 +113,17 @@ func NewColorable(file *os.File) io.Writer { var csbi consoleScreenBufferInfo handle := syscall.Handle(file.Fd()) procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} + return &writer{out: file, handle: handle, oldattr: csbi.attributes, curattr: csbi.attributes, oldpos: coord{0, 0}} } return file } -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. +// NewColorableStdout returns new instance of writer which handles escape sequence for stdout. func NewColorableStdout() io.Writer { return NewColorable(os.Stdout) } -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. +// NewColorableStderr returns new instance of writer which handles escape sequence for stderr. func NewColorableStderr() io.Writer { return NewColorable(os.Stderr) } @@ -434,11 +435,15 @@ func atoiWithDefault(s string, def int) (int, error) { } // Write writes data on console -func (w *Writer) Write(data []byte) (n int, err error) { +func (w *writer) Write(data []byte) (n int, err error) { w.mutex.Lock() defer w.mutex.Unlock() var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + if w.rest.Len() == 0 && bytes.IndexByte(data, 0x1b) == -1 { + w.out.Write(data) + return len(data), nil + } handle := w.handle @@ -517,7 +522,7 @@ loop: w.rest.Reset() break } - buf.Write([]byte(string(c))) + buf.WriteByte(c) } if m == 0 { break loop @@ -560,7 +565,7 @@ loop: } procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'E': - n, err = strconv.Atoi(buf.String()) + n, err = atoiWithDefault(buf.String(), 1) if err != nil { continue } @@ -569,7 +574,7 @@ loop: csbi.cursorPosition.y += short(n) procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'F': - n, err = strconv.Atoi(buf.String()) + n, err = atoiWithDefault(buf.String(), 1) if err != nil { continue } @@ -678,11 +683,11 @@ loop: procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) case 'm': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - attr := csbi.attributes + attr := w.curattr cs := buf.String() if cs == "" { procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) + w.curattr = w.oldattr continue } token := strings.Split(cs, ";") @@ -814,9 +819,12 @@ loop: attr |= backgroundBlue } } - procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) } } + if attr != w.curattr { + procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) + w.curattr = attr + } case 'h': var ci consoleCursorInfo cs := buf.String() @@ -834,6 +842,8 @@ loop: w.althandle = syscall.Handle(h) if w.althandle != 0 { handle = w.althandle + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.curattr = csbi.attributes } } } @@ -853,6 +863,8 @@ loop: syscall.CloseHandle(w.althandle) w.althandle = 0 handle = w.handle + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.curattr = csbi.attributes } } case 's': diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go index 7402e0618..569113eac 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_others.go +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -1,5 +1,5 @@ -//go:build (appengine || js || nacl || tinygo || wasm) && !windows -// +build appengine js nacl tinygo wasm +//go:build (appengine || js || nacl || tinygo || wasm || wasip1 || wasip2 || haiku) && !windows +// +build appengine js nacl tinygo wasm wasip1 wasip2 haiku // +build !windows package isatty diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go index 8e3c99171..5f29c11dd 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_windows.go +++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -31,6 +31,10 @@ func init() { if procGetFileInformationByHandleEx.Find() != nil { procGetFileInformationByHandleEx = nil } + // Check if NtQueryObject is available. + if procNtQueryObject.Find() != nil { + procNtQueryObject = nil + } } // IsTerminal return true if the file descriptor is terminal. @@ -43,6 +47,7 @@ func IsTerminal(fd uintptr) bool { // Check pipe name is used for cygwin/msys2 pty. // Cygwin/MSYS2 PTY has a name like: // \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +// On Windows 7 a trailing suffix (e.g. "-nat") may be appended. func isCygwinPipeName(name string) bool { token := strings.Split(name, "-") if len(token) < 5 { @@ -72,13 +77,19 @@ func isCygwinPipeName(name string) bool { return false } + for _, t := range token[5:] { + if t == "" { + return false + } + } + return true } -// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler +// getFileNameByHandle use the undocumented ntdll NtQueryObject to get file full name from file handler // since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion // guys are using Windows XP, this is a workaround for those guys, it will also work on system from -// Windows vista to 10 +// Windows Vista to 10 // see https://stackoverflow.com/a/18792477 for details func getFileNameByHandle(fd uintptr) (string, error) { if procNtQueryObject == nil { diff --git a/vendor/github.com/oapi-codegen/echo-middleware/Makefile b/vendor/github.com/oapi-codegen/echo-middleware/Makefile index 91dd690ee..1e7c31c27 100644 --- a/vendor/github.com/oapi-codegen/echo-middleware/Makefile +++ b/vendor/github.com/oapi-codegen/echo-middleware/Makefile @@ -10,7 +10,7 @@ help: @echo " tidy tidy go mod" $(GOBIN)/golangci-lint: - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v1.59.0 + curl -sSfL https://golangci-lint.run/install.sh | sh -s -- -b $(GOBIN) v2.12.2 .PHONY: tools tools: $(GOBIN)/golangci-lint @@ -19,7 +19,7 @@ lint: tools $(GOBIN)/golangci-lint run ./... lint-ci: tools - $(GOBIN)/golangci-lint run ./... --out-format=github-actions --timeout=5m + $(GOBIN)/golangci-lint run ./... --output.text.path=stdout --timeout=5m generate: go generate ./... @@ -30,3 +30,6 @@ test: tidy: @echo "tidy..." go mod tidy + +# the CI scripts require a rule by this name +tidy-ci: tidy diff --git a/vendor/github.com/oapi-codegen/echo-middleware/README.md b/vendor/github.com/oapi-codegen/echo-middleware/README.md index a2077ed39..ed1e00e6e 100644 --- a/vendor/github.com/oapi-codegen/echo-middleware/README.md +++ b/vendor/github.com/oapi-codegen/echo-middleware/README.md @@ -1,7 +1,77 @@ -# Echo Middleware +# OpenAPI Validation Middleware for `labstack/echo` servers + +Middleware for the [Echo web server](https://github.com/labstack/echo) to perform validation of incoming requests via an OpenAPI specification. + +This project is a lightweight wrapper over the excellent [kin-openapi](https://github.com/getkin/kin-openapi) library's [`openapi3filter` package](https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3filter). + +This is _intended_ to be used with code that's generated through [`oapi-codegen`](https://github.com/oapi-codegen/oapi-codegen), but should work otherwise. ⚠️ This README may be for the latest development version, which may contain unreleased changes. Please ensure you're looking at the README for the latest release version. -Middleware for the [Echo web server](https://github.com/labstack/echo) for use with [deepmap/oapi-codegen](https://github.com/deepmap/oapi-codegen). +## Usage + +You can add the middleware to your project with: + +```sh +go get github.com/oapi-codegen/echo-middleware +``` + +There is a full example of usage in [the Go doc for this project](https://pkg.go.dev/github.com/oapi-codegen/echo-middleware#pkg-examples). + +A simplified version of this code is as follows: + +```go +rawSpec := ` +openapi: "3.0.0" +# ... +` +spec, _ := openapi3.NewLoader().LoadFromData([]byte(rawSpec)) + +// NOTE that we need to make sure that the `Servers` aren't set, otherwise the OpenAPI validation middleware will validate that the `Host` header (of incoming requests) are targeting known `Servers` in the OpenAPI spec +// See also: Options#SilenceServersWarning +spec.Servers = nil + +e := echo.New() +e.POST("/resource", func(c echo.Context) error { + fmt.Printf("%s /resource was called\n", c.Request().Method) + + return c.NoContent(http.StatusNoContent) +}) + +// create middleware +mw := middleware.OapiRequestValidatorWithOptions(spec, &middleware.Options{ + Options: openapi3filter.Options{ + AuthenticationFunc: authenticationFunc, + }, +}) + +e.Use(mw) + +// now all HTTP routes will be handled by the middleware, and any requests that are invalid will be rejected +``` + +## FAQs + +### What versions of Echo does this support? + +| Version | Supported? | +| -- | -- | +| `github.com/labstack/echo/v4` | ✅ | +| `github.com/labstack/echo/v5` | ❌ [Issue](https://github.com/oapi-codegen/echo-middleware/issues/37) | + + +### "This doesn't support ..." / "I think it's a bug that ..." + +As this project is a lightweight wrapper over [kin-openapi](https://github.com/getkin/kin-openapi)'s [`openapi3filter` package](https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3filter), it's _likely_ that any bugs/features are better sent upstream. + +However, it's worth raising an issue here instead, as it'll allow us to triage it before it goes to the kin-openapi maintainers. + +Additionally, as `oapi-codegen` contains [a number of middleware modules](https://github.com/search?q=org%3Aoapi-codegen+middleware&type=repositories), we'll very likely want to implement the same functionality across all the middlewares, so it may take a bit more coordination to get the changes in across our middlewares. + +### I've just updated my version of `kin-openapi`, and now I can't build my code 😠 + +The [kin-openapi](https://github.com/getkin/kin-openapi) project - which we 💜 for providing a great library and set of tooling for interacting with OpenAPI - is a pre-v1 release, which means that they're within their rights to push breaking changes. + +This may lead to breakage in your consuming code, and if so, sorry that's happened! -Licensed under the Apache-2.0. +We'll be aware of the issue, and will work to update both the core `oapi-codegen` and the middlewares accordingly. diff --git a/vendor/github.com/oapi-codegen/echo-middleware/oapi_validate.go b/vendor/github.com/oapi-codegen/echo-middleware/oapi_validate.go index 11dd1eb42..f48ad69fc 100644 --- a/vendor/github.com/oapi-codegen/echo-middleware/oapi_validate.go +++ b/vendor/github.com/oapi-codegen/echo-middleware/oapi_validate.go @@ -1,17 +1,10 @@ -// Copyright 2019 DeepMap, Inc. +// Provide HTTP middleware functionality to validate that incoming requests conform to a given OpenAPI 3.x specification. // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// This provides middleware for an echo/v4 HTTP server. // -// http://www.apache.org/licenses/LICENSE-2.0 +// This package is a lightweight wrapper over https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3filter from https://pkg.go.dev/github.com/getkin/kin-openapi. // -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// This is _intended_ to be used with code that's generated through https://pkg.go.dev/github.com/oapi-codegen/oapi-codegen, but should work otherwise. package echomiddleware import ( @@ -20,6 +13,7 @@ import ( "fmt" "log" "net/http" + "net/url" "os" "strings" @@ -46,44 +40,69 @@ func OapiValidatorFromYamlFile(path string) (echo.MiddlewareFunc, error) { return nil, fmt.Errorf("error reading %s: %w", path, err) } - swagger, err := openapi3.NewLoader().LoadFromData(data) + spec, err := openapi3.NewLoader().LoadFromData(data) if err != nil { - return nil, fmt.Errorf("error parsing %s as Swagger YAML: %w", path, err) + return nil, fmt.Errorf("error parsing %s as OpenAPI YAML: %w", path, err) } - return OapiRequestValidator(swagger), nil + return OapiRequestValidator(spec), nil } -// OapiRequestValidator creates a validator from a swagger object. -func OapiRequestValidator(swagger *openapi3.T) echo.MiddlewareFunc { - return OapiRequestValidatorWithOptions(swagger, nil) +// OapiRequestValidator Creates the middleware to validate that incoming requests match the given OpenAPI 3.x spec, with a default set of configuration. +func OapiRequestValidator(spec *openapi3.T) echo.MiddlewareFunc { + return OapiRequestValidatorWithOptions(spec, nil) } // ErrorHandler is called when there is an error in validation type ErrorHandler func(c echo.Context, err *echo.HTTPError) error -// MultiErrorHandler is called when oapi returns a MultiError type +// MultiErrorHandler is called when the OpenAPI filter returns an openapi3.MultiError (https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3#MultiError) type MultiErrorHandler func(openapi3.MultiError) *echo.HTTPError // Options to customize request validation. These are passed through to // openapi3filter. type Options struct { - ErrorHandler ErrorHandler - Options openapi3filter.Options - ParamDecoder openapi3filter.ContentParameterDecoder - UserData interface{} - Skipper echomiddleware.Skipper + // ErrorHandler is called when a validation error occurs. + // + // If not provided, `http.Error` will be called + ErrorHandler ErrorHandler + // Options contains any configuration for the underlying `openapi3filter` + Options openapi3filter.Options + // ParamDecoder is the openapi3filter.ContentParameterDecoder to be used for the decoding of the request body + // + // If unset, a default will be used + ParamDecoder openapi3filter.ContentParameterDecoder + // UserData is any user-specified data to inject into the context.Context, which is then passed in to the validation function. + // + // Set on the Context with the key `UserDataKey`. + UserData any + // Skipper an echo Skipper to allow skipping the middleware. + Skipper echomiddleware.Skipper + // MultiErrorHandler is called when there is an openapi3.MultiError (https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3#MultiError) returned by the `openapi3filter`. + // + // If not provided `defaultMultiErrorHandler` will be used. MultiErrorHandler MultiErrorHandler - // SilenceServersWarning allows silencing a warning for https://github.com/deepmap/oapi-codegen/issues/882 that reports when an OpenAPI spec has `spec.Servers != nil` + // SilenceServersWarning allows silencing a warning for https://github.com/oapi-codegen/oapi-codegen/issues/882 that reports when an OpenAPI spec has `spec.Servers != nil` SilenceServersWarning bool + // DoNotValidateServers ensures that there is no Host validation performed (see `SilenceServersWarning` and https://github.com/deepmap/oapi-codegen/issues/882 for more details) + DoNotValidateServers bool + // Prefix is stripped from the request path before validation. This is useful when the API is mounted under a sub-path + // (e.g. "/api") that isn't part of the OpenAPI spec's paths. The prefix must start with "/" if set. + Prefix string } -// OapiRequestValidatorWithOptions creates a validator from a swagger object, with validation options -func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) echo.MiddlewareFunc { - if swagger.Servers != nil && (options == nil || !options.SilenceServersWarning) { - log.Println("WARN: OapiRequestValidatorWithOptions called with an OpenAPI spec that has `Servers` set. This may lead to an HTTP 400 with `no matching operation was found` when sending a valid request, as the validator performs `Host` header validation. If you're expecting `Host` header validation, you can silence this warning by setting `Options.SilenceServersWarning = true`. See https://github.com/deepmap/oapi-codegen/issues/882 for more information.") +// OapiRequestValidatorWithOptions Creates the middleware to validate that incoming requests match the given OpenAPI 3.x spec, allowing explicit configuration. +// +// NOTE that this may panic if the OpenAPI spec isn't valid, or if it cannot be used to create the middleware +func OapiRequestValidatorWithOptions(spec *openapi3.T, options *Options) echo.MiddlewareFunc { + if options != nil && options.DoNotValidateServers { + spec.Servers = nil + } + + if spec.Servers != nil && (options == nil || !options.SilenceServersWarning) { + log.Println("WARN: OapiRequestValidatorWithOptions called with an OpenAPI spec that has `Servers` set. This may lead to an HTTP 400 with `no matching operation was found` when sending a valid request, as the validator performs `Host` header validation. If you're expecting `Host` header validation, you can silence this warning by setting `Options.SilenceServersWarning = true`. See https://github.com/oapi-codegen/oapi-codegen/issues/882 for more information.") } - router, err := gorillamux.NewRouter(swagger) + router, err := gorillamux.NewRouter(spec) if err != nil { panic(err) } @@ -111,10 +130,22 @@ func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) echo // of validating a request. func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options *Options) *echo.HTTPError { req := ctx.Request() + + if options != nil && options.Prefix != "" { + // Clone the request so downstream handlers still see the original path. + clone := req.Clone(req.Context()) + clone.URL.Path = strings.TrimPrefix(clone.URL.Path, options.Prefix) + req = clone + } + route, pathParams, err := router.FindRoute(req) // We failed to find a matching route for the request. if err != nil { + if errors.Is(err, routers.ErrMethodNotAllowed) { + return echo.NewHTTPError(http.StatusMethodNotAllowed) + } + switch e := err.(type) { case *routers.RouteError: // We've got a bad request, the path requested doesn't match @@ -128,6 +159,15 @@ func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options } } + // gorillamux uses UseEncodedPath(), so path parameters are returned in + // their percent-encoded form. Unescape them before passing to + // openapi3filter, which expects decoded values. + for k, v := range pathParams { + if unescaped, err := url.PathUnescape(v); err == nil { + pathParams[k] = unescaped + } + } + validationInput := &openapi3filter.RequestValidationInput{ Request: req, PathParams: pathParams, @@ -202,7 +242,7 @@ func GetEchoContext(c context.Context) echo.Context { return eCtx } -func GetUserData(c context.Context) interface{} { +func GetUserData(c context.Context) any { return c.Value(UserDataKey) } diff --git a/vendor/github.com/oapi-codegen/echo-middleware/renovate.json b/vendor/github.com/oapi-codegen/echo-middleware/renovate.json index c4e1abbd1..9744b1c4e 100644 --- a/vendor/github.com/oapi-codegen/echo-middleware/renovate.json +++ b/vendor/github.com/oapi-codegen/echo-middleware/renovate.json @@ -2,5 +2,10 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "local>oapi-codegen/renovate-config" - ] + ], + "gomod": { + "constraints": { + "go": "1.24" + } + } } diff --git a/vendor/github.com/oapi-codegen/echo-middleware/test_spec.yaml b/vendor/github.com/oapi-codegen/echo-middleware/test_spec.yaml index 1f847d756..7b9059187 100644 --- a/vendor/github.com/oapi-codegen/echo-middleware/test_spec.yaml +++ b/vendor/github.com/oapi-codegen/echo-middleware/test_spec.yaml @@ -3,7 +3,7 @@ info: version: 1.0.0 title: TestServer servers: - - url: http://deepmap.ai + - url: http://test.hostname paths: /resource: get: @@ -39,6 +39,32 @@ paths: properties: name: type: string + /resource/maxlength/{param}: + get: + operationId: getMaxLengthResourceParameter + parameters: + - name: param + in: path + required: true + schema: + type: string + maxLength: 1 + responses: + '204': + description: success + /resource/pattern/{param}: + get: + operationId: getPatternResourceParameter + parameters: + - name: param + in: path + required: true + schema: + type: string + pattern: '^\+[0-9]+$' + responses: + '204': + description: success /protected_resource: get: operationId: getProtectedResource diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen/oapi-codegen.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen/oapi-codegen.go index 78bea5b1e..c1299881b 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen/oapi-codegen.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen/oapi-codegen.go @@ -14,6 +14,7 @@ package main import ( + "bytes" "flag" "fmt" "os" @@ -23,13 +24,13 @@ import ( "runtime/debug" "strings" - "gopkg.in/yaml.v2" + "go.yaml.in/yaml/v3" "github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen" "github.com/oapi-codegen/oapi-codegen/v2/pkg/util" ) -func errExit(format string, args ...interface{}) { +func errExit(format string, args ...any) { if !strings.HasSuffix(format, "\n") { format = format + "\n" } @@ -58,7 +59,6 @@ var ( flagExcludeSchemas string flagResponseTypeSuffix string flagAliasTypes bool - flagInitialismOverrides bool ) type configuration struct { @@ -102,7 +102,7 @@ func main() { // All flags below are deprecated, and will be removed in a future release. Please do not // update their behavior. flag.StringVar(&flagGenerate, "generate", "types,client,server,spec", - `Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune", "fiber", "iris", "std-http".`) + `Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune", "fiber", "fiber-v3", "iris", "std-http".`) flag.StringVar(&flagIncludeTags, "include-tags", "", "Only include operations with the given tags. Comma-separated list of tags.") flag.StringVar(&flagExcludeTags, "exclude-tags", "", "Exclude operations that are tagged with the given tags. Comma-separated list of tags.") flag.StringVar(&flagIncludeOperationIDs, "include-operation-ids", "", "Only include operations with the given operation-ids. Comma-separated list of operation-ids.") @@ -112,7 +112,6 @@ func main() { flag.StringVar(&flagExcludeSchemas, "exclude-schemas", "", "A comma separated list of schemas which must be excluded from generation.") flag.StringVar(&flagResponseTypeSuffix, "response-type-suffix", "", "The suffix used for responses types.") flag.BoolVar(&flagAliasTypes, "alias-types", false, "Alias type declarations if possible.") - flag.BoolVar(&flagInitialismOverrides, "initialism-overrides", false, "Use initialism overrides.") flag.Parse() @@ -158,10 +157,14 @@ func main() { errExit("error reading config file '%s': %v\n", flagConfigFile, err) } var oldConfig oldConfiguration - oldErr := yaml.UnmarshalStrict(configFile, &oldConfig) + oldDec := yaml.NewDecoder(bytes.NewReader(configFile)) + oldDec.KnownFields(true) + oldErr := oldDec.Decode(&oldConfig) var newConfig configuration - newErr := yaml.UnmarshalStrict(configFile, &newConfig) + newDec := yaml.NewDecoder(bytes.NewReader(configFile)) + newDec.KnownFields(true) + newErr := newDec.Decode(&newConfig) // If one of the two files parses, but the other fails, we know the // answer. @@ -272,21 +275,36 @@ func main() { } if warnings := opts.Generate.Warnings(); len(warnings) > 0 { - out := "WARNING: A number of warning(s) were returned when validating the GenerateOptions:" + var out strings.Builder + out.WriteString("WARNING: A number of warning(s) were returned when validating the GenerateOptions:") for k, v := range warnings { - out += "\n- " + k + ": " + v + out.WriteString("\n- " + k + ": " + v) } - _, _ = fmt.Fprint(os.Stderr, out) + _, _ = fmt.Fprint(os.Stderr, out.String()) + } + + if warnings := opts.Warnings(); len(warnings) > 0 { + var out strings.Builder + out.WriteString("WARNING: A number of cross-field configuration warning(s) were returned:") + for k, v := range warnings { + out.WriteString("\n- " + k + ": " + v) + } + out.WriteString("\n") + + _, _ = fmt.Fprint(os.Stderr, out.String()) } // If the user asked to output configuration, output it to stdout and exit if flagOutputConfig { - buf, err := yaml.Marshal(opts) - if err != nil { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + enc.SetIndent(2) + if err := enc.Encode(opts); err != nil { errExit("error YAML marshaling configuration: %v\n", err) } - fmt.Print(string(buf)) + _ = enc.Close() + fmt.Print(buf.String()) return } @@ -305,29 +323,31 @@ func main() { errExit("error loading swagger spec in %s\n: %s\n", flag.Arg(0), err) } - if strings.HasPrefix(swagger.OpenAPI, "3.1.") { - fmt.Fprintln(os.Stderr, "WARNING: You are using an OpenAPI 3.1.x specification, which is not yet supported by oapi-codegen (https://github.com/oapi-codegen/oapi-codegen/issues/373) and so some functionality may not be available. Until oapi-codegen supports OpenAPI 3.1, it is recommended to downgrade your spec to 3.0.x") - } - if len(noVCSVersionOverride) > 0 { opts.NoVCSVersionOverride = &noVCSVersionOverride } - code, err := codegen.Generate(swagger, opts.Configuration) - if err != nil { - errExit("error generating code: %s\n", err) - } + code, genErr := codegen.Generate(swagger, opts.Configuration) - if opts.OutputFile != "" { - if err := os.MkdirAll(filepath.Dir(opts.OutputFile), 0o755); err != nil { - errExit("error unable to create directory: %s\n", err) - } - err = os.WriteFile(opts.OutputFile, []byte(code), 0o644) - if err != nil { - errExit("error writing generated code to file: %s\n", err) + // Always emit any generated code to the requested destination, even when + // generation returned an error (e.g. the formatter rejected the output). + // Writing to the output file lets the user inspect the broken source + // directly instead of having it interleaved with stderr. + if code != "" { + if opts.OutputFile != "" { + if err := os.MkdirAll(filepath.Dir(opts.OutputFile), 0o755); err != nil { + errExit("error unable to create directory: %s\n", err) + } + if err := os.WriteFile(opts.OutputFile, []byte(code), 0o644); err != nil { + errExit("error writing generated code to file: %s\n", err) + } + } else { + fmt.Print(code) } - } else { - fmt.Print(code) + } + + if genErr != nil { + errExit("error generating code: %s\n", genErr) } } @@ -458,8 +478,6 @@ func updateConfigFromFlags(cfg *configuration) error { cfg.OutputFile = flagOutputFile } - cfg.OutputOptions.InitialismOverrides = flagInitialismOverrides - return nil } @@ -509,8 +527,12 @@ func generationTargets(cfg *codegen.Configuration, targets []string) error { opts.ChiServer = true case "fiber-server", "fiber": opts.FiberServer = true + case "fiber-v3-server", "fiber-v3": + opts.FiberV3Server = true case "server", "echo-server", "echo": opts.EchoServer = true + case "echo5", "echo5-server": + opts.Echo5Server = true case "gin", "gin-server": opts.GinServer = true case "gorilla", "gorilla-server": diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/codegen.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/codegen.go index 6e602ea05..90ced55a3 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/codegen.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/codegen.go @@ -19,13 +19,17 @@ import ( "bytes" "context" "embed" + "errors" "fmt" + "go/scanner" "io" "io/fs" + "maps" "net/http" "os" + "regexp" "runtime/debug" - "sort" + "slices" "strings" "text/template" "time" @@ -44,12 +48,31 @@ var templates embed.FS // globalState stores all global state. Please don't put global state anywhere // else so that we can easily track it. var globalState struct { - options Configuration - spec *openapi3.T + options Configuration + spec *openapi3.T + // is31 is true when the loaded spec declares OpenAPI version >=3.1. + // All version-aware behavior (e.g. nullable detection, webhook emission) + // reads from this field. Do NOT expose this field to templates -- + // templates are version-blind and consume pre-computed derived fields + // (e.g. Schema.Nullable) populated by version-aware Go helpers. + // Adding `is31` to TemplateFunctions or branching on the OpenAPI + // version inside a template is a layering violation. + is31 bool importMapping importMap // initialismsMap stores initialisms as "lower(initialism) -> initialism" map. // List of initialisms was taken from https://staticcheck.io/docs/configuration/options/#initialisms. initialismsMap map[string]string + // typeMapping is the merged type mapping (defaults + user overrides). + typeMapping TypeMapping + // resolvedNames maps schema path strings (e.g. "components/schemas/Pet") + // to their resolved Go type names, assigned by the multi-pass name resolver. + resolvedNames map[string]string + // resolvedClientWrapperNames maps operationID to the resolved Go type name + // for client response wrapper types (e.g., "createChatCompletion" -> "CreateChatCompletionResponseWrapper"). + resolvedClientWrapperNames map[string]string + // streamingContentTypeRegexes are the compiled regexes (defaults + user) + // used by ResponseContentDefinition.IsStreamingContentType. + streamingContentTypeRegexes []*regexp.Regexp } // goImport represents a go package to be imported in the generated code @@ -88,25 +111,41 @@ func (im importMap) GoImports() []string { func constructImportMapping(importMapping map[string]string) importMap { var ( - pathToName = map[string]string{} - result = importMap{} + pathToImport = importMap{} + result = importMap{} ) { - var packagePaths []string - for _, packageName := range importMapping { - packagePaths = append(packagePaths, packageName) - } - sort.Strings(packagePaths) + packagePaths := slices.Collect(maps.Values(importMapping)) + slices.Sort(packagePaths) for _, packagePath := range packagePaths { - if _, ok := pathToName[packagePath]; !ok && packagePath != importMappingCurrentPackage { - pathToName[packagePath] = fmt.Sprintf("externalRef%d", len(pathToName)) + if _, ok := pathToImport[packagePath]; !ok && packagePath != importMappingCurrentPackage { + split := strings.Split(packagePath, " ") + if len(split) == 2 { + // if we have 2 parts, we assume both the package name and path are provided, and we use them as is + pathToImport[packagePath] = goImport{ + Name: split[0], + Path: split[1], + } + } else { + // otherwise, we auto-generate a package name based on the order of the imports, to ensure deterministic output + pathToImport[packagePath] = goImport{ + Name: fmt.Sprintf("externalRef%d", len(pathToImport)), + Path: packagePath, + } + } } } } for specPath, packagePath := range importMapping { - result[specPath] = goImport{Name: pathToName[packagePath], Path: packagePath} + if packagePath == importMappingCurrentPackage { + result[specPath] = goImport{ + Path: importMappingCurrentPackage, + } + } else { + result[specPath] = pathToImport[packagePath] + } } return result } @@ -118,7 +157,13 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { // This is global state globalState.options = opts globalState.spec = spec + globalState.is31 = spec.IsOpenAPI31OrLater() globalState.importMapping = constructImportMapping(opts.ImportMapping) + if opts.OutputOptions.TypeMapping != nil { + globalState.typeMapping = DefaultTypeMapping.Merge(*opts.OutputOptions.TypeMapping) + } else { + globalState.typeMapping = DefaultTypeMapping + } filterOperationsByTag(spec, opts) filterOperationsByOperationID(spec, opts) @@ -126,6 +171,14 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { pruneUnusedComponents(spec) } + // Reject spec values that cannot be represented in the generated Go source + // (names, media types, enum values, extension hints containing quotes, + // backticks, or control characters). Run after filtering/pruning so only + // values that will actually be emitted are considered. + if err := ValidateSpec(spec); err != nil { + return "", err + } + // if we are provided an override for the response type suffix update it if opts.OutputOptions.ResponseTypeSuffix != "" { responseTypeSuffix = opts.OutputOptions.ResponseTypeSuffix @@ -148,12 +201,42 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { globalState.initialismsMap = makeInitialismsMap(opts.OutputOptions.AdditionalInitialisms) + // Compile streaming-content-type patterns (defaults merged with user-supplied). + // Validate() already caught syntax errors, but surface any regression here too. + streamingRegexes, err := compileStreamingContentTypes(opts.OutputOptions.StreamingContentTypes) + if err != nil { + return "", err + } + globalState.streamingContentTypeRegexes = streamingRegexes + + // Multi-pass name resolution: gather all schemas, then resolve names globally. + // Only enabled when resolve-type-name-collisions is set. + if opts.OutputOptions.ResolveTypeNameCollisions { + gathered := GatherSchemas(spec, opts) + globalState.resolvedNames = ResolveNames(gathered) + // Build a separate operationID -> wrapper name lookup for genResponseTypeName. + // Keys must use the normalized operationID (via nameNormalizer) because + // OperationDefinition.OperationId is normalized before templates run. + globalState.resolvedClientWrapperNames = make(map[string]string) + for _, gs := range gathered { + if gs.Context == ContextClientResponseWrapper && gs.OperationID != "" { + if name, ok := globalState.resolvedNames[gs.Path.String()]; ok { + normalizedOpID := nameNormalizer(gs.OperationID) + globalState.resolvedClientWrapperNames[normalizedOpID] = name + } + } + } + } else { + globalState.resolvedNames = nil + globalState.resolvedClientWrapperNames = nil + } + // This creates the golang templates text package TemplateFunctions["opts"] = func() Configuration { return globalState.options } t := template.New("oapi-codegen").Funcs(TemplateFunctions) // This parses all of our own template files into the template object // above - err := LoadTemplates(templates, t) + err = LoadTemplates(templates, t) if err != nil { return "", fmt.Errorf("error parsing oapi-codegen templates: %w", err) } @@ -173,24 +256,67 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { } } - ops, err := OperationDefinitions(spec, opts.OutputOptions.InitialismOverrides) + ops, err := OperationDefinitions(spec) if err != nil { return "", fmt.Errorf("error creating operation definitions: %w", err) } - xGoTypeImports, err := OperationImports(ops) + // Webhooks (OpenAPI 3.1+) flow through the same OperationDefinition + // shape as paths but render via webhook-specific templates. The + // gather walks swagger.Webhooks unconditionally -- the map is only + // populated by kin-openapi for 3.1+ documents, so 3.0 specs return + // an empty slice naturally. allOps is used for cross-cutting passes + // (type defs, import gathering); ops and webhookOps are passed + // separately to path-vs-webhook template generators. + webhookOps, err := WebhookOperationDefinitions(spec) + if err != nil { + return "", fmt.Errorf("error creating webhook operation definitions: %w", err) + } + + // Callbacks (OpenAPI 3.0+) are nested under path operations. Like + // webhooks they render via initiator/receiver templates, but they + // are NOT version-gated -- callbacks have been part of the spec + // since 3.0. Gather is no-op for specs without any callbacks. + callbackOps, err := CallbackOperationDefinitions(spec) + if err != nil { + return "", fmt.Errorf("error creating callback operation definitions: %w", err) + } + allOps := append(append(append([]OperationDefinition{}, ops...), webhookOps...), callbackOps...) + + xGoTypeImports, err := OperationImports(allOps) if err != nil { return "", fmt.Errorf("error getting operation imports: %w", err) } + // Type and constant emission is gated by Generate.Models. Both + // component-derived types (from #/components) and op-derived types + // (Params/RequestBody/hoisted-ResponseBody) flow through this gate + // today; that conflation is historical but kept for backwards + // compatibility (changing it would alter the symbols emitted by + // `models: true`-alone configs and break downstream code in the wild). var typeDefinitions, constantDefinitions string if opts.Generate.Models { - typeDefinitions, err = GenerateTypeDefinitions(t, spec, ops, opts.OutputOptions.ExcludeSchemas) + componentTypes, err := collectComponentTypes(t, spec, opts.OutputOptions.ExcludeSchemas) if err != nil { - return "", fmt.Errorf("error generating type definitions: %w", err) + return "", fmt.Errorf("error collecting component types: %w", err) + } + componentDecls, err := GenerateTypes(t, componentTypes) + if err != nil { + return "", fmt.Errorf("error generating code for type definitions: %w", err) + } + + // Pass allOps (regular paths + webhooks + callbacks) so op-derived + // types from webhook/callback operations are emitted too. + opTypes, err := collectOperationTypes(allOps) + if err != nil { + return "", fmt.Errorf("error collecting operation types: %w", err) + } + opDecls, err := GenerateTypesForOperations(t, allOps) + if err != nil { + return "", fmt.Errorf("error generating Go types for operations: %w", err) } - constantDefinitions, err = GenerateConstants(t, ops) + constantDefinitions, err = GenerateConstants(t, spec) if err != nil { return "", fmt.Errorf("error generating constants: %w", err) } @@ -199,15 +325,48 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if err != nil { return "", fmt.Errorf("error getting type definition imports: %w", err) } - MergeImports(xGoTypeImports, imprts) + maps.Copy(xGoTypeImports, imprts) + + // Boilerplate (enum Valid(), union accessors, additionalProperties + // marshalers) scans the union of all declared types so methods are + // emitted for inline types living inside operations too. + allEmitted := slices.Concat(componentTypes, opTypes) + enumsOut, allOfOut, unionOut, unionAndAdditionalOut, err := renderBoilerplate(t, allEmitted) + if err != nil { + return "", err + } + // Preserve historical concatenation order: + // enums, component decls, op decls, allOf, union, union+additional. + typeDefinitions = strings.Join([]string{enumsOut, componentDecls, opDecls, allOfOut, unionOut, unionAndAdditionalOut}, "") } var serverURLsDefinitions string if opts.Generate.ServerURLs { - serverURLsDefinitions, err = GenerateServerURLs(t, spec) + // Server-URL enum-typed variables are routed through the same + // typedef.tmpl + constants.tmpl pipelines as any other + // enum-bearing schema, so they get matching `type X string`, + // `const ( … )` block, and `Valid()` method. We do this here + // (rather than in collectComponentTypes) so the types are + // emitted even when `generate.models` is disabled. + serverURLEnumTypes, err := BuildServerURLTypeDefinitions(spec) + if err != nil { + return "", fmt.Errorf("error generating Go types for server URL variables: %w", err) + } + serverURLEnumTypeDecls, err := GenerateTypes(t, serverURLEnumTypes) + if err != nil { + return "", fmt.Errorf("error generating type declarations for server URL variables: %w", err) + } + serverURLEnumConstants, err := GenerateEnums(t, serverURLEnumTypes) + if err != nil { + return "", fmt.Errorf("error generating enums for server URL variables: %w", err) + } + + serverURLsBody, err := GenerateServerURLs(t, spec) if err != nil { return "", fmt.Errorf("error generating Server URLs: %w", err) } + + serverURLsDefinitions = serverURLEnumTypeDecls + serverURLEnumConstants + serverURLsBody } var irisServerOut string @@ -226,6 +385,14 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { } } + var echo5ServerOut string + if opts.Generate.Echo5Server { + echo5ServerOut, err = GenerateEcho5Server(t, ops) + if err != nil { + return "", fmt.Errorf("error generating Go handlers for Paths: %w", err) + } + } + var chiServerOut string if opts.Generate.ChiServer { chiServerOut, err = GenerateChiServer(t, ops) @@ -242,6 +409,14 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { } } + var fiberV3ServerOut string + if opts.Generate.FiberV3Server { + fiberV3ServerOut, err = GenerateFiberV3Server(t, ops) + if err != nil { + return "", fmt.Errorf("error generating Go handlers for Paths: %w", err) + } + } + var ginServerOut string if opts.Generate.GinServer { ginServerOut, err = GenerateGinServer(t, ops) @@ -270,7 +445,7 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if opts.Generate.Strict { var responses []ResponseDefinition if spec.Components != nil { - responses, err = GenerateResponseDefinitions("", spec.Components.Responses) + responses, err = GenerateResponseDefinitions("", spec.Components.Responses, "") if err != nil { return "", fmt.Errorf("error generation response definitions for schema: %w", err) } @@ -302,6 +477,197 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { } } + // Webhook initiator pairs with the path Client. Emitted only when + // Generate.Client is on AND the spec has webhooks (3.1+). + var webhookInitiatorOut string + if opts.Generate.Client && len(webhookOps) > 0 { + webhookInitiatorOut, err = GenerateWebhookInitiator(t, webhookOps) + if err != nil { + return "", fmt.Errorf("error generating webhook initiator: %w", err) + } + } + + // Webhook receiver (stdhttp) pairs with the path StdHTTPServer. + // Emitted only when Generate.StdHTTPServer is on AND the spec has + // webhooks (3.1+). Uses the unified stdhttp receiver template, + // parameterized with prefix "Webhook". + var stdHTTPWebhookReceiverOut string + if opts.Generate.StdHTTPServer && len(webhookOps) > 0 { + stdHTTPWebhookReceiverOut, err = GenerateStdHTTPReceiver(t, "Webhook", webhookOps) + if err != nil { + return "", fmt.Errorf("error generating stdhttp webhook receiver: %w", err) + } + } + + // Webhook receiver (chi) -- chi shares stdhttp's (w, r) handler + // signature, so the receiver shape is identical; only the template + // path differs. Emitted only when Generate.ChiServer is on. + var chiWebhookReceiverOut string + if opts.Generate.ChiServer && len(webhookOps) > 0 { + chiWebhookReceiverOut, err = GenerateChiReceiver(t, "Webhook", webhookOps) + if err != nil { + return "", fmt.Errorf("error generating chi webhook receiver: %w", err) + } + } + + // Webhook receiver (gorilla/mux) -- same (w, r) signature as + // stdhttp/chi. + var gorillaWebhookReceiverOut string + if opts.Generate.GorillaServer && len(webhookOps) > 0 { + gorillaWebhookReceiverOut, err = GenerateGorillaReceiver(t, "Webhook", webhookOps) + if err != nil { + return "", fmt.Errorf("error generating gorilla webhook receiver: %w", err) + } + } + + // Webhook receiver (echo v4) -- (ctx echo.Context) error signature. + var echoWebhookReceiverOut string + if opts.Generate.EchoServer && len(webhookOps) > 0 { + echoWebhookReceiverOut, err = GenerateEchoReceiver(t, "Webhook", webhookOps) + if err != nil { + return "", fmt.Errorf("error generating echo webhook receiver: %w", err) + } + } + + // Webhook receiver (echo v5) -- (ctx *echo.Context) error signature. + var echo5WebhookReceiverOut string + if opts.Generate.Echo5Server && len(webhookOps) > 0 { + echo5WebhookReceiverOut, err = GenerateEcho5Receiver(t, "Webhook", webhookOps) + if err != nil { + return "", fmt.Errorf("error generating echo5 webhook receiver: %w", err) + } + } + + // Webhook receiver (gin) -- (c *gin.Context) signature. + var ginWebhookReceiverOut string + if opts.Generate.GinServer && len(webhookOps) > 0 { + ginWebhookReceiverOut, err = GenerateGinReceiver(t, "Webhook", webhookOps) + if err != nil { + return "", fmt.Errorf("error generating gin webhook receiver: %w", err) + } + } + + // Webhook receiver (fiber v2) -- (c *fiber.Ctx) error signature. + var fiberWebhookReceiverOut string + if opts.Generate.FiberServer && len(webhookOps) > 0 { + fiberWebhookReceiverOut, err = GenerateFiberReceiver(t, "Webhook", webhookOps) + if err != nil { + return "", fmt.Errorf("error generating fiber webhook receiver: %w", err) + } + } + + // Webhook receiver (fiber v3) -- (c fiber.Ctx) error signature. + var fiberV3WebhookReceiverOut string + if opts.Generate.FiberV3Server && len(webhookOps) > 0 { + fiberV3WebhookReceiverOut, err = GenerateFiberV3Receiver(t, "Webhook", webhookOps) + if err != nil { + return "", fmt.Errorf("error generating fiber v3 webhook receiver: %w", err) + } + } + + // Webhook receiver (iris) -- (ctx iris.Context) signature. + var irisWebhookReceiverOut string + if opts.Generate.IrisServer && len(webhookOps) > 0 { + irisWebhookReceiverOut, err = GenerateIrisReceiver(t, "Webhook", webhookOps) + if err != nil { + return "", fmt.Errorf("error generating iris webhook receiver: %w", err) + } + } + + // Callback initiator pairs with the path Client. Emitted whenever + // Generate.Client is on and the spec declares any callbacks -- + // callbacks predate 3.1 so this is not version-gated. + var callbackInitiatorOut string + if opts.Generate.Client && len(callbackOps) > 0 { + callbackInitiatorOut, err = GenerateCallbackInitiator(t, callbackOps) + if err != nil { + return "", fmt.Errorf("error generating callback initiator: %w", err) + } + } + + // Callback receiver (stdhttp) pairs with the path StdHTTPServer. + // Same reasoning as the initiator: not version-gated. Uses the + // unified stdhttp receiver template with prefix "Callback". + var stdHTTPCallbackReceiverOut string + if opts.Generate.StdHTTPServer && len(callbackOps) > 0 { + stdHTTPCallbackReceiverOut, err = GenerateStdHTTPReceiver(t, "Callback", callbackOps) + if err != nil { + return "", fmt.Errorf("error generating stdhttp callback receiver: %w", err) + } + } + + // Callback receiver (chi). + var chiCallbackReceiverOut string + if opts.Generate.ChiServer && len(callbackOps) > 0 { + chiCallbackReceiverOut, err = GenerateChiReceiver(t, "Callback", callbackOps) + if err != nil { + return "", fmt.Errorf("error generating chi callback receiver: %w", err) + } + } + + // Callback receiver (gorilla/mux). + var gorillaCallbackReceiverOut string + if opts.Generate.GorillaServer && len(callbackOps) > 0 { + gorillaCallbackReceiverOut, err = GenerateGorillaReceiver(t, "Callback", callbackOps) + if err != nil { + return "", fmt.Errorf("error generating gorilla callback receiver: %w", err) + } + } + + // Callback receiver (echo v4). + var echoCallbackReceiverOut string + if opts.Generate.EchoServer && len(callbackOps) > 0 { + echoCallbackReceiverOut, err = GenerateEchoReceiver(t, "Callback", callbackOps) + if err != nil { + return "", fmt.Errorf("error generating echo callback receiver: %w", err) + } + } + + // Callback receiver (echo v5). + var echo5CallbackReceiverOut string + if opts.Generate.Echo5Server && len(callbackOps) > 0 { + echo5CallbackReceiverOut, err = GenerateEcho5Receiver(t, "Callback", callbackOps) + if err != nil { + return "", fmt.Errorf("error generating echo5 callback receiver: %w", err) + } + } + + // Callback receiver (gin). + var ginCallbackReceiverOut string + if opts.Generate.GinServer && len(callbackOps) > 0 { + ginCallbackReceiverOut, err = GenerateGinReceiver(t, "Callback", callbackOps) + if err != nil { + return "", fmt.Errorf("error generating gin callback receiver: %w", err) + } + } + + // Callback receiver (fiber v2). + var fiberCallbackReceiverOut string + if opts.Generate.FiberServer && len(callbackOps) > 0 { + fiberCallbackReceiverOut, err = GenerateFiberReceiver(t, "Callback", callbackOps) + if err != nil { + return "", fmt.Errorf("error generating fiber callback receiver: %w", err) + } + } + + // Callback receiver (fiber v3). + var fiberV3CallbackReceiverOut string + if opts.Generate.FiberV3Server && len(callbackOps) > 0 { + fiberV3CallbackReceiverOut, err = GenerateFiberV3Receiver(t, "Callback", callbackOps) + if err != nil { + return "", fmt.Errorf("error generating fiber v3 callback receiver: %w", err) + } + } + + // Callback receiver (iris). + var irisCallbackReceiverOut string + if opts.Generate.IrisServer && len(callbackOps) > 0 { + irisCallbackReceiverOut, err = GenerateIrisReceiver(t, "Callback", callbackOps) + if err != nil { + return "", fmt.Errorf("error generating iris callback receiver: %w", err) + } + } + var inlinedSpec string if opts.Generate.EmbeddedSpec { inlinedSpec, err = GenerateInlinedSpec(t, globalState.importMapping, spec) @@ -353,6 +719,18 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if err != nil { return "", fmt.Errorf("error writing client: %w", err) } + if webhookInitiatorOut != "" { + _, err = w.WriteString(webhookInitiatorOut) + if err != nil { + return "", fmt.Errorf("error writing webhook initiator: %w", err) + } + } + if callbackInitiatorOut != "" { + _, err = w.WriteString(callbackInitiatorOut) + if err != nil { + return "", fmt.Errorf("error writing callback initiator: %w", err) + } + } } if opts.Generate.IrisServer { @@ -360,7 +738,18 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if err != nil { return "", fmt.Errorf("error writing server path handlers: %w", err) } - + if irisWebhookReceiverOut != "" { + _, err = w.WriteString(irisWebhookReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing iris webhook receiver: %w", err) + } + } + if irisCallbackReceiverOut != "" { + _, err = w.WriteString(irisCallbackReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing iris callback receiver: %w", err) + } + } } if opts.Generate.EchoServer { @@ -368,6 +757,37 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if err != nil { return "", fmt.Errorf("error writing server path handlers: %w", err) } + if echoWebhookReceiverOut != "" { + _, err = w.WriteString(echoWebhookReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing echo webhook receiver: %w", err) + } + } + if echoCallbackReceiverOut != "" { + _, err = w.WriteString(echoCallbackReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing echo callback receiver: %w", err) + } + } + } + + if opts.Generate.Echo5Server { + _, err = w.WriteString(echo5ServerOut) + if err != nil { + return "", fmt.Errorf("error writing server path handlers: %w", err) + } + if echo5WebhookReceiverOut != "" { + _, err = w.WriteString(echo5WebhookReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing echo5 webhook receiver: %w", err) + } + } + if echo5CallbackReceiverOut != "" { + _, err = w.WriteString(echo5CallbackReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing echo5 callback receiver: %w", err) + } + } } if opts.Generate.ChiServer { @@ -375,6 +795,18 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if err != nil { return "", fmt.Errorf("error writing server path handlers: %w", err) } + if chiWebhookReceiverOut != "" { + _, err = w.WriteString(chiWebhookReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing chi webhook receiver: %w", err) + } + } + if chiCallbackReceiverOut != "" { + _, err = w.WriteString(chiCallbackReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing chi callback receiver: %w", err) + } + } } if opts.Generate.FiberServer { @@ -382,6 +814,37 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if err != nil { return "", fmt.Errorf("error writing server path handlers: %w", err) } + if fiberWebhookReceiverOut != "" { + _, err = w.WriteString(fiberWebhookReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing fiber webhook receiver: %w", err) + } + } + if fiberCallbackReceiverOut != "" { + _, err = w.WriteString(fiberCallbackReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing fiber callback receiver: %w", err) + } + } + } + + if opts.Generate.FiberV3Server { + _, err = w.WriteString(fiberV3ServerOut) + if err != nil { + return "", fmt.Errorf("error writing server path handlers: %w", err) + } + if fiberV3WebhookReceiverOut != "" { + _, err = w.WriteString(fiberV3WebhookReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing fiber v3 webhook receiver: %w", err) + } + } + if fiberV3CallbackReceiverOut != "" { + _, err = w.WriteString(fiberV3CallbackReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing fiber v3 callback receiver: %w", err) + } + } } if opts.Generate.GinServer { @@ -389,6 +852,18 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if err != nil { return "", fmt.Errorf("error writing server path handlers: %w", err) } + if ginWebhookReceiverOut != "" { + _, err = w.WriteString(ginWebhookReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing gin webhook receiver: %w", err) + } + } + if ginCallbackReceiverOut != "" { + _, err = w.WriteString(ginCallbackReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing gin callback receiver: %w", err) + } + } } if opts.Generate.GorillaServer { @@ -396,6 +871,18 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if err != nil { return "", fmt.Errorf("error writing server path handlers: %w", err) } + if gorillaWebhookReceiverOut != "" { + _, err = w.WriteString(gorillaWebhookReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing gorilla webhook receiver: %w", err) + } + } + if gorillaCallbackReceiverOut != "" { + _, err = w.WriteString(gorillaCallbackReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing gorilla callback receiver: %w", err) + } + } } if opts.Generate.StdHTTPServer { @@ -403,6 +890,18 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { if err != nil { return "", fmt.Errorf("error writing server path handlers: %w", err) } + if stdHTTPWebhookReceiverOut != "" { + _, err = w.WriteString(stdHTTPWebhookReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing stdhttp webhook receiver: %w", err) + } + } + if stdHTTPCallbackReceiverOut != "" { + _, err = w.WriteString(stdHTTPCallbackReceiverOut) + if err != nil { + return "", fmt.Errorf("error writing stdhttp callback receiver: %w", err) + } + } } if opts.Generate.Strict { @@ -435,105 +934,167 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { outBytes, err := imports.Process(opts.PackageName+".go", []byte(goCode), nil) if err != nil { - return "", fmt.Errorf("error formatting Go code %s: %w", goCode, err) + errLine := -1 + var scanErr scanner.ErrorList + if errors.As(err, &scanErr) && scanErr.Len() > 0 { + errLine = scanErr[0].Pos.Line + } + if errLine > 0 { + return goCode, fmt.Errorf("error formatting Go code at line %d: %w", errLine, err) + } + return goCode, fmt.Errorf("error formatting Go code: %w", err) } return string(outBytes), nil } -func GenerateTypeDefinitions(t *template.Template, swagger *openapi3.T, ops []OperationDefinition, excludeSchemas []string) (string, error) { - var allTypes []TypeDefinition - if swagger.Components != nil { - schemaTypes, err := GenerateTypesForSchemas(t, swagger.Components.Schemas, excludeSchemas) +// collectComponentTypes returns the TypeDefinitions collected from +// components/{schemas,parameters,responses,requestBodies,securitySchemes}. +// These are the "outer" types gated by Generate.Models. +func collectComponentTypes(t *template.Template, swagger *openapi3.T, excludeSchemas []string) ([]TypeDefinition, error) { + if swagger.Components == nil { + return nil, nil + } + schemaTypes, err := GenerateTypesForSchemas(t, swagger.Components.Schemas, excludeSchemas) + if err != nil { + return nil, fmt.Errorf("error generating Go types for component schemas: %w", err) + } + paramTypes, err := GenerateTypesForParameters(t, swagger.Components.Parameters) + if err != nil { + return nil, fmt.Errorf("error generating Go types for component parameters: %w", err) + } + responseTypes, err := GenerateTypesForResponses(t, swagger.Components.Responses) + if err != nil { + return nil, fmt.Errorf("error generating Go types for component responses: %w", err) + } + bodyTypes, err := GenerateTypesForRequestBodies(t, swagger.Components.RequestBodies) + if err != nil { + return nil, fmt.Errorf("error generating Go types for component request bodies: %w", err) + } + var securitySchemeTypes []TypeDefinition + if globalState.options.Compatibility.EnableAuthScopesOnContext { + securitySchemeTypes, err = GenerateTypesForSecuritySchemes(t, swagger.Components.SecuritySchemes) if err != nil { - return "", fmt.Errorf("error generating Go types for component schemas: %w", err) + return nil, fmt.Errorf("error generating Go types for component security schemes: %w", err) } + } + allTypes := append(schemaTypes, paramTypes...) + allTypes = append(allTypes, responseTypes...) + allTypes = append(allTypes, bodyTypes...) + allTypes = append(allTypes, securitySchemeTypes...) + return allTypes, nil +} - paramTypes, err := GenerateTypesForParameters(t, swagger.Components.Parameters) +// collectOperationTypes returns the operation-derived TypeDefinitions that +// flow into the boilerplate scan: op.TypeDefinitions (Params, hoisted +// AdditionalTypes from params/bodies/response schemas) plus the per- +// response AdditionalTypeDefinitions from client response wrappers. +// These are the "outer" types gated by AnyOperationGenerator. +// +// Note: these are NOT the same set of types that param-types.tmpl / +// request-bodies.tmpl declare — those templates iterate ops directly. +// The slice returned here is what the *boilerplate* (enums, union and +// additionalProperties marshalers) must scan so methods don't go missing +// for inline types living inside operations. +func collectOperationTypes(ops []OperationDefinition) ([]TypeDefinition, error) { + var out []TypeDefinition + for _, op := range ops { + out = append(out, op.TypeDefinitions...) + respDefs, err := op.GetResponseTypeDefinitions() if err != nil { - return "", fmt.Errorf("error generating Go types for component parameters: %w", err) + return nil, fmt.Errorf("error collecting response type definitions for %s: %w", op.OperationId, err) } - allTypes = append(schemaTypes, paramTypes...) - - responseTypes, err := GenerateTypesForResponses(t, swagger.Components.Responses) - if err != nil { - return "", fmt.Errorf("error generating Go types for component responses: %w", err) + for _, rd := range respDefs { + out = append(out, rd.AdditionalTypeDefinitions...) } - allTypes = append(allTypes, responseTypes...) - - bodyTypes, err := GenerateTypesForRequestBodies(t, swagger.Components.RequestBodies) - if err != nil { - return "", fmt.Errorf("error generating Go types for component request bodies: %w", err) - } - allTypes = append(allTypes, bodyTypes...) - } - - // Go through all operations, and add their types to allTypes, so that we can - // scan all of them for enums. Operation definitions are handled differently - // from the rest, so let's keep track of enumTypes separately, which will contain - // all types needed to be scanned for enums, which includes those within operations. - enumTypes := allTypes - for _, op := range ops { - enumTypes = append(enumTypes, op.TypeDefinitions...) - } - - operationsOut, err := GenerateTypesForOperations(t, ops) - if err != nil { - return "", fmt.Errorf("error generating Go types for component request bodies: %w", err) - } - - enumsOut, err := GenerateEnums(t, enumTypes) - if err != nil { - return "", fmt.Errorf("error generating code for type enums: %w", err) } + return out, nil +} - typesOut, err := GenerateTypes(t, allTypes) +// renderBoilerplate runs the enum, additionalProperties, union, and +// union+additionalProperties passes over the union of all emitted types. +// These passes are "inner" — they emit methods/constants subordinate to +// whichever outer types were declared. +func renderBoilerplate(t *template.Template, allEmitted []TypeDefinition) (enumsOut, allOfOut, unionOut, unionAndAdditionalOut string, err error) { + enumsOut, err = GenerateEnums(t, allEmitted) if err != nil { - return "", fmt.Errorf("error generating code for type definitions: %w", err) + return "", "", "", "", fmt.Errorf("error generating code for type enums: %w", err) } - - allOfBoilerplate, err := GenerateAdditionalPropertyBoilerplate(t, allTypes) + allOfOut, err = GenerateAdditionalPropertyBoilerplate(t, allEmitted) if err != nil { - return "", fmt.Errorf("error generating allOf boilerplate: %w", err) + return "", "", "", "", fmt.Errorf("error generating allOf boilerplate: %w", err) } - - unionBoilerplate, err := GenerateUnionBoilerplate(t, allTypes) + unionOut, err = GenerateUnionBoilerplate(t, allEmitted) if err != nil { - return "", fmt.Errorf("error generating union boilerplate: %w", err) + return "", "", "", "", fmt.Errorf("error generating union boilerplate: %w", err) } - - unionAndAdditionalBoilerplate, err := GenerateUnionAndAdditionalProopertiesBoilerplate(t, allTypes) + unionAndAdditionalOut, err = GenerateUnionAndAdditionalProopertiesBoilerplate(t, allEmitted) if err != nil { - return "", fmt.Errorf("error generating boilerplate for union types with additionalProperties: %w", err) + return "", "", "", "", fmt.Errorf("error generating boilerplate for union types with additionalProperties: %w", err) } - - typeDefinitions := strings.Join([]string{enumsOut, typesOut, operationsOut, allOfBoilerplate, unionBoilerplate, unionAndAdditionalBoilerplate}, "") - return typeDefinitions, nil + return enumsOut, allOfOut, unionOut, unionAndAdditionalOut, nil } // GenerateConstants generates operation ids, context keys, paths, etc. to be exported as constants -func GenerateConstants(t *template.Template, ops []OperationDefinition) (string, error) { - constants := Constants{ - SecuritySchemeProviderNames: []string{}, - } +// +// Scopes constants are derived from components/securitySchemes rather than +// from the operations' security requirements (which are filtered to defined +// schemes anyway), so that a spec holding shared definitions with no paths +// still exports the constants other packages alias via import-mapping. +func GenerateConstants(t *template.Template, swagger *openapi3.T) (string, error) { + var constants Constants - providerNameMap := map[string]struct{}{} - for _, op := range ops { - for _, def := range op.SecurityDefinitions { - providerName := SanitizeGoIdentity(def.ProviderName) - providerNameMap[providerName] = struct{}{} + if swagger.Components != nil { + for _, schemeName := range SortedSecuritySchemeKeys(swagger.Components.SecuritySchemes) { + provider := SecuritySchemeProvider{Name: SanitizeGoIdentity(schemeName)} + alias := importedSecuritySchemeScopes(swagger.Components.SecuritySchemes[schemeName].Ref) + if alias == securitySchemeScopesConstant(provider.Name) { + // The scheme $refs a spec that import-mapping assigns to the + // current package under the same name: the sibling config + // generating that spec into this package already declares + // the constant, so re-declaring it here would collide. + continue + } + provider.ImportedScopes = alias + constants.SecuritySchemeProviders = append(constants.SecuritySchemeProviders, provider) } } - var providerNames []string - for providerName := range providerNameMap { - providerNames = append(providerNames, providerName) - } - - sort.Strings(providerNames) + return GenerateTemplates([]string{"constants.tmpl"}, t, constants) +} - constants.SecuritySchemeProviderNames = append(constants.SecuritySchemeProviderNames, providerNames...) +// securitySchemeScopesConstant returns the name of the generated scopes +// context-key constant for a security scheme name. It must mirror the name +// construction in constants.tmpl (`sanitizeGoIdentity | ucFirst` + "Scopes"). +func securitySchemeScopesConstant(schemeName string) string { + return UppercaseFirstCharacter(SanitizeGoIdentity(schemeName)) + "Scopes" +} - return GenerateTemplates([]string{"constants.tmpl"}, t, constants) +// importedSecuritySchemeScopes resolves a security scheme's $ref through +// import-mapping to the scopes constant declared by the package generated +// from the ref'd document. It returns "" when the scheme must be declared +// locally: it is defined inline, the ref is internal, or the ref'd document +// has no import-mapping entry (each package then keeps its own declaration, +// matching the behavior from before typed context keys existed). For a +// document mapped to the current package ("-") the returned constant is +// unqualified — the sibling config generating that document declares it. +func importedSecuritySchemeScopes(ref string) string { + if ref == "" || strings.HasPrefix(ref, "#") { + return "" + } + pathParts := strings.Split(ref, "#") + if len(pathParts) != 2 { + return "" + } + goPkg, ok := globalState.importMapping[pathParts[0]] + if !ok { + return "" + } + componentParts := strings.Split(pathParts[1], "/") + constName := securitySchemeScopesConstant(componentParts[len(componentParts)-1]) + if goPkg.Path == importMappingCurrentPackage { + return constName + } + return fmt.Sprintf("%s.%s", goPkg.Name, constName) } // GenerateTypesForSchemas generates type definitions for any custom types defined in the @@ -561,6 +1122,10 @@ func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3. return nil, fmt.Errorf("error making name for components/schemas/%s: %w", schemaName, err) } + if resolved := resolvedNameForComponent("schemas", schemaName); resolved != "" { + goTypeName = resolved + } + types = append(types, TypeDefinition{ JsonName: schemaName, TypeName: goTypeName, @@ -589,6 +1154,10 @@ func GenerateTypesForParameters(t *template.Template, params map[string]*openapi return nil, fmt.Errorf("error making name for components/parameters/%s: %w", paramName, err) } + if resolved := resolvedNameForComponent("parameters", paramName); resolved != "" { + goTypeName = resolved + } + typeDef := TypeDefinition{ JsonName: paramName, Schema: goType, @@ -621,22 +1190,31 @@ func GenerateTypesForResponses(t *template.Template, responses openapi3.Response // handle media types that conform to JSON. Other responses should // simply be specified as strings or byte arrays. response := responseOrRef.Value + content := response.Content - jsonCount := 0 - for mediaType := range response.Content { - if util.IsMediaTypeJson(mediaType) { - jsonCount++ - } - } - - SortedMapKeys := SortedMapKeys(response.Content) + SortedMapKeys := SortedMapKeys(content) for _, mediaType := range SortedMapKeys { response := response.Content[mediaType] if !util.IsMediaTypeJson(mediaType) { continue } - goType, err := GenerateGoSchema(response.Schema, []string{responseName}) + // When a response has multiple JSON content types, include the + // content type in the schema path so that inline types (e.g., + // oneOf union members) get unique names per content type. + // See the matching logic in GetResponseTypeDefinitions. + // + // We only add the content type segment when collision resolution + // is enabled (resolve-type-name-collisions) and jsonCount > 1, + // to avoid changing type names for existing users. Ideally the + // media type would always be part of the path for consistency. + // TODO: revisit this at the next major version change — + // always include the media type in the schema path. + schemaPath := []string{responseName} + if suffix := responseMediaTypeSuffix(content, mediaType); suffix != "" && globalState.options.OutputOptions.ResolveTypeNameCollisions { + schemaPath = append(schemaPath, suffix) + } + goType, err := GenerateGoSchema(response.Schema, schemaPath) if err != nil { return nil, fmt.Errorf("error generating Go type for schema in response %s: %w", responseName, err) } @@ -646,6 +1224,10 @@ func GenerateTypesForResponses(t *template.Template, responses openapi3.Response return nil, fmt.Errorf("error making name for components/responses/%s: %w", responseName, err) } + if resolved := resolvedNameForComponent("responses", responseName, mediaType); resolved != "" { + goTypeName = resolved + } + typeDef := TypeDefinition{ JsonName: responseName, Schema: goType, @@ -661,11 +1243,12 @@ func GenerateTypesForResponses(t *template.Template, responses openapi3.Response typeDef.TypeName = SchemaNameToTypeName(refType) } - if jsonCount > 1 { - typeDef.TypeName = typeDef.TypeName + mediaTypeToCamelCase(mediaType) + if suffix := responseMediaTypeSuffix(content, mediaType); suffix != "" { + typeDef.TypeName = typeDef.TypeName + suffix } types = append(types, typeDef) + types = append(types, goType.AdditionalTypes...) } } return types, nil @@ -682,7 +1265,8 @@ func GenerateTypesForRequestBodies(t *template.Template, bodies map[string]*open // As for responses, we will only generate Go code for JSON bodies, // the other body formats are up to the user. response := requestBodyRef.Value - for mediaType, body := range response.Content { + for _, mediaType := range SortedMapKeys(response.Content) { + body := response.Content[mediaType] if !util.IsMediaTypeJson(mediaType) { continue } @@ -697,6 +1281,10 @@ func GenerateTypesForRequestBodies(t *template.Template, bodies map[string]*open return nil, fmt.Errorf("error making name for components/schemas/%s: %w", requestBodyName, err) } + if resolved := resolvedNameForComponent("requestBodies", requestBodyName, mediaType); resolved != "" { + goTypeName = resolved + } + typeDef := TypeDefinition{ JsonName: requestBodyName, Schema: goType, @@ -712,8 +1300,39 @@ func GenerateTypesForRequestBodies(t *template.Template, bodies map[string]*open typeDef.TypeName = SchemaNameToTypeName(refType) } types = append(types, typeDef) + types = append(types, goType.AdditionalTypes...) + } + } + return types, nil +} + +// GenerateTypesForSecuritySchemes generates type definitions for any custom types defined in the +// components/securitySchemes section of the Swagger spec. +func GenerateTypesForSecuritySchemes(t *template.Template, schemes map[string]*openapi3.SecuritySchemeRef) ([]TypeDefinition, error) { + var types []TypeDefinition + + for _, schemeName := range SortedSecuritySchemeKeys(schemes) { + if importedSecuritySchemeScopes(schemes[schemeName].Ref) != "" { + // The scheme $refs a spec assigned to another package by + // import-mapping. That package declares the context key type; + // the scopes constant alias emitted by GenerateConstants + // carries it over, so no local type is declared. + continue } + // Generate a type to be used as a key in context.WithValue + goTypeName := LowercaseFirstCharacter(SchemaNameToTypeName(schemeName)) + "ContextKey" + goType := Schema{ + GoType: "string", + Description: fmt.Sprintf("is the context key for %s security scheme", schemeName), + } + + types = append(types, TypeDefinition{ + JsonName: schemeName, + TypeName: goTypeName, + Schema: goType, + }) } + return types, nil } @@ -726,12 +1345,11 @@ func GenerateTypes(t *template.Template, types []TypeDefinition) (string, error) for _, typ := range types { if prevType, found := m[typ.TypeName]; found { // If type names collide, we need to see if they refer to the same - // exact type definition, in which case, we can de-dupe. If they don't - // match, we error out. + // exact type definition, in which case, we can de-dupe. If they + // don't match, we error out. if TypeDefinitionsEquivalent(prevType, typ) { continue } - // We want to create an error when we try to define the same type twice. return "", fmt.Errorf("duplicate typename '%s' detected, can't auto-rename, "+ "please use x-go-name to specify your own name for one of them", typ.TypeName) } @@ -750,6 +1368,63 @@ func GenerateTypes(t *template.Template, types []TypeDefinition) (string, error) return GenerateTemplates([]string{"typedef.tmpl"}, t, context) } +// resolvedNameForComponent looks up the resolved Go type name for a component +// identified by its section (e.g., "schemas", "parameters") and name. +// For content-bearing sections (responses, requestBodies), an optional +// contentType can be provided to match the exact media type entry. +// Returns empty string if no resolved name is available. +func resolvedNameForComponent(section, name string, contentType ...string) string { + if len(globalState.resolvedNames) == 0 { + return "" + } + + // Direct key match for schemas, parameters, headers + key := "components/" + section + "/" + name + if resolved, ok := globalState.resolvedNames[key]; ok { + return resolved + } + + // For responses and requestBodies, the path includes content type info. + // If a specific content type was provided, do an exact match. + if len(contentType) > 0 && contentType[0] != "" { + exactKey := key + "/content/" + contentType[0] + if resolved, ok := globalState.resolvedNames[exactKey]; ok { + return resolved + } + } + + // Fall back to prefix match for callers that don't specify content type. + // Sort matching keys so the result is deterministic across runs. + prefix := key + "/" + var matches []string + for k := range globalState.resolvedNames { + if strings.HasPrefix(k, prefix) { + matches = append(matches, k) + } + } + if len(matches) > 0 { + if len(matches) > 1 { + slices.Sort(matches) + } + return globalState.resolvedNames[matches[0]] + } + + return "" +} + +// resolvedNameForRefPath looks up the resolved Go type name for a $ref path +// like "#/components/responses/Foo", optionally scoped to a specific content type. +func resolvedNameForRefPath(refPath, contentType string) string { + if len(globalState.resolvedNames) == 0 || !strings.HasPrefix(refPath, "#/") { + return "" + } + parts := strings.Split(refPath, "/") + if len(parts) != 4 || parts[1] != "components" { + return "" + } + return resolvedNameForComponent(parts[2], parts[3], contentType) +} + func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error) { enums := []EnumDefinition{} @@ -773,31 +1448,68 @@ func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error) Schema: tp.Schema, TypeName: tp.TypeName, ValueWrapper: wrapper, - PrefixTypeName: globalState.options.Compatibility.AlwaysPrefixEnumValues, + PrefixTypeName: globalState.options.Compatibility.AlwaysPrefixEnumValues || tp.ForceEnumPrefix, }) } } // Now, go through all the enums, and figure out if we have conflicts with // any others. - for i := range enums { - // Look through all other enums not compared so far. Make sure we don't - // compare against self. - e1 := enums[i] - for j := i + 1; j < len(enums); j++ { - e2 := enums[j] - - for e1key := range e1.GetValues() { - _, found := e2.GetValues()[e1key] - if found { - e1.PrefixTypeName = true - e2.PrefixTypeName = true - enums[i] = e1 - enums[j] = e2 - break + if globalState.options.Compatibility.DisableEnumValueConflictResolution { + // Legacy behavior: compare generated constant names via GetValues(). + // This is order-dependent because GetValues() reflects already-applied + // prefixes, so enums processed later (in sorted schema-name order) may + // miss conflicts with enums that were prefixed in an earlier iteration. + // Preserved here as an escape hatch for users who need to keep their + // existing generated output. + for i := range enums { + e1 := enums[i] + for j := i + 1; j < len(enums); j++ { + e2 := enums[j] + for e1key := range e1.GetValues() { + if _, found := e2.GetValues()[e1key]; found { + e1.PrefixTypeName = true + e2.PrefixTypeName = true + enums[i] = e1 + enums[j] = e2 + break + } } } } + } else { + // Resolve cross-enum constant-name collisions by prefixing each + // conflicting enum's constants with its type name. Prefixing can itself + // introduce a new collision — a prefixed name like "Enum1One" matching + // another enum's raw value — so we iterate to a fixed point. + for { + changed := false + for i := range enums { + for j := i + 1; j < len(enums); j++ { + if enums[i].PrefixTypeName && enums[j].PrefixTypeName { + continue + } + if enumsConflict(enums[i], enums[j]) { + if !enums[i].PrefixTypeName { + enums[i].PrefixTypeName = true + changed = true + } + if !enums[j].PrefixTypeName { + enums[j].PrefixTypeName = true + changed = true + } + } + } + } + if !changed { + break + } + } + } + + // Check each enum against global type names and self-conflict. + for i := range enums { + e1 := enums[i] // now see if this enum conflicts with any global type names. for _, tp := range types { @@ -823,7 +1535,34 @@ func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error) // Now see if enums conflict with any non-enum typenames - return GenerateTemplates([]string{"constants.tmpl"}, t, Constants{EnumDefinitions: enums}) + return GenerateTemplates([]string{"constants.tmpl"}, t, Constants{ + EnumDefinitions: enums, + SkipEnumValidate: globalState.options.OutputOptions.SkipEnumValidate, + }) +} + +// enumsConflict reports whether two enums must be disambiguated by prefixing. +// +// They conflict when they share a raw value name, or when their current +// effective constant names collide. The raw-value check uses Schema.EnumValues, +// which is immutable, so the result is independent of any prefixing already +// applied earlier in the same pass — otherwise prefixing one enum could mask +// its conflict with a third (the order-dependent bug this resolution fixes). +// The effective-name check uses GetValues() to catch collisions *introduced* by +// prefixing, e.g. a prefixed "Enum1One" matching another enum's raw value. +func enumsConflict(a, b EnumDefinition) bool { + for name := range a.Schema.EnumValues { + if _, ok := b.Schema.EnumValues[name]; ok { + return true + } + } + bValues := b.GetValues() + for name := range a.GetValues() { + if _, ok := bValues[name]; ok { + return true + } + } + return false } // GenerateImports generates our import statements and package definition. @@ -852,12 +1591,14 @@ func GenerateImports(t *template.Template, externalImports []string, packageName ModuleName string Version string AdditionalImports []AdditionalImport + RouterImports []AdditionalImport }{ ExternalImports: externalImports, PackageName: packageName, ModuleName: modulePath, Version: moduleVersion, AdditionalImports: globalState.options.AdditionalImports, + RouterImports: globalState.options.Generate.RouterImports(), } return GenerateTemplates([]string{"imports.tmpl"}, t, context) @@ -893,7 +1634,12 @@ func GenerateAdditionalPropertyBoilerplate(t *template.Template, typeDefs []Type func GenerateUnionBoilerplate(t *template.Template, typeDefs []TypeDefinition) (string, error) { var filteredTypes []TypeDefinition + seen := map[string]bool{} for _, t := range typeDefs { + if seen[t.TypeName] { + continue + } + seen[t.TypeName] = true if len(t.Schema.UnionElements) != 0 { filteredTypes = append(filteredTypes, t) } @@ -914,7 +1660,12 @@ func GenerateUnionBoilerplate(t *template.Template, typeDefs []TypeDefinition) ( func GenerateUnionAndAdditionalProopertiesBoilerplate(t *template.Template, typeDefs []TypeDefinition) (string, error) { var filteredTypes []TypeDefinition + seen := map[string]bool{} for _, t := range typeDefs { + if seen[t.TypeName] { + continue + } + seen[t.TypeName] = true if len(t.Schema.UnionElements) != 0 && t.Schema.HasAdditionalProperties { filteredTypes = append(filteredTypes, t) } @@ -1026,14 +1777,14 @@ func OperationSchemaImports(s *Schema) (map[string]goImport, error) { if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } imprts, err := GoSchemaImports(&openapi3.SchemaRef{Value: s.OAPISchema}) if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) return res, nil } @@ -1046,7 +1797,7 @@ func OperationImports(ops []OperationDefinition) (map[string]goImport, error) { if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } } @@ -1055,7 +1806,7 @@ func OperationImports(ops []OperationDefinition) (map[string]goImport, error) { if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } for _, b := range op.Responses { @@ -1064,7 +1815,7 @@ func OperationImports(ops []OperationDefinition) (map[string]goImport, error) { if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } } @@ -1099,7 +1850,7 @@ func GetTypeDefinitionsImports(swagger *openapi3.T, excludeSchemas []string) (ma } for _, imprts := range []map[string]goImport{schemaImports, reqBodiesImports, responsesImports, parametersImports} { - MergeImports(res, imprts) + maps.Copy(res, imprts) } return res, nil } @@ -1107,33 +1858,36 @@ func GetTypeDefinitionsImports(swagger *openapi3.T, excludeSchemas []string) (ma func GoSchemaImports(schemas ...*openapi3.SchemaRef) (map[string]goImport, error) { res := map[string]goImport{} for _, sref := range schemas { - if sref == nil || sref.Value == nil || IsGoTypeReference(sref.Ref) { + if sref == nil { return nil, nil } + if gi, err := ParseGoImportExtension(sref); err != nil { return nil, err - } else { - if gi != nil { - res[gi.String()] = *gi - } + } else if gi != nil { + res[gi.String()] = *gi + } + + if sref.Value == nil || IsGoTypeReference(sref.Ref) { + continue } schemaVal := sref.Value - t := schemaVal.Type + t := schemaPrimaryType(schemaVal.Type) if t.Slice() == nil || t.Is("object") { for _, v := range schemaVal.Properties { imprts, err := GoSchemaImports(v) if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } } else if t.Is("array") { imprts, err := GoSchemaImports(schemaVal.Items) if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } } return res, nil @@ -1154,7 +1908,7 @@ func GetSchemaImports(schemas map[string]*openapi3.SchemaRef, excludeSchemas []s if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } return res, nil } @@ -1172,7 +1926,7 @@ func GetRequestBodiesImports(bodies map[string]*openapi3.RequestBodyRef) (map[st if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } } return res, nil @@ -1191,7 +1945,7 @@ func GetResponsesImports(responses map[string]*openapi3.ResponseRef) (map[string if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } } return res, nil @@ -1207,7 +1961,7 @@ func GetParametersImports(params map[string]*openapi3.ParameterRef) (map[string] if err != nil { return nil, err } - MergeImports(res, imprts) + maps.Copy(res, imprts) } return res, nil } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/configuration.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/configuration.go index 1d9ff3eae..0eb375613 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/configuration.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/configuration.go @@ -4,8 +4,19 @@ import ( "errors" "fmt" "reflect" + "regexp" + "strings" ) +// defaultStreamingContentTypes are the regex patterns matched against +// response Content-Type to decide when the strict-server should emit a +// flush-per-chunk streaming path. User-supplied patterns are merged on top. +var defaultStreamingContentTypes = []string{ + "text/event-stream", + "application/jsonl", + "application/x-ndjson", +} + type AdditionalImport struct { Alias string `yaml:"alias,omitempty"` Package string `yaml:"package"` @@ -47,9 +58,15 @@ func (o Configuration) Validate() error { if o.Generate.FiberServer { nServers++ } + if o.Generate.FiberV3Server { + nServers++ + } if o.Generate.EchoServer { nServers++ } + if o.Generate.Echo5Server { + nServers++ + } if o.Generate.GorillaServer { nServers++ } @@ -82,6 +99,16 @@ func (o Configuration) Validate() error { } } + // import-mapping keys are the paths of $ref'd documents (a relative + // file path or URL). A JSON pointer key can never match anything — + // references within the same document always resolve to the package + // being generated — so it is a configuration mistake, not a mapping. + for _, specPath := range SortedMapKeys(o.ImportMapping) { + if strings.HasPrefix(specPath, "#") { + errs = append(errs, fmt.Errorf("`import-mapping` key %q is a JSON pointer, but keys must be the path or URL of a $ref'd document; references within the same document cannot be remapped to another package — see https://github.com/oapi-codegen/oapi-codegen/tree/main/examples/import-mapping", specPath)) + } + } + err := errors.Join(errs...) if err != nil { return fmt.Errorf("failed to validate configuration: %w", err) @@ -90,6 +117,34 @@ func (o Configuration) Validate() error { return nil } +// Warnings returns informational diagnostics about cross-field configuration +// combinations that aren't outright errors but are likely to produce +// generated code that doesn't compile, or that requires the user to know a +// specific multi-config convention. The warnings are surfaced on stderr by +// the CLI so the user notices before running `go build`. +func (o Configuration) Warnings() map[string]string { + warnings := make(map[string]string) + + // `generate-types-for-anonymous-schemas` hoists inline schemas into + // named Go types. The declarations of those named types are emitted + // only when `generate.models: true`. If the flag is set with + // `models: false` alongside a client or server generator, the + // generated code will reference type names that this config does + // not declare. + // + // This is fine when a sibling config (e.g. a `models: true` config + // emitting into the same Go package) declares the types, but is a + // hard compile failure if no sibling is present. We can't tell the + // two cases apart from a single config, so we warn rather than + // error — split-config users can ignore the warning; single-config + // users get a heads-up before `go build` fails. + if o.OutputOptions.GenerateTypesForAnonymousSchemas && !o.Generate.Models && o.Generate.AnyOperationGenerator() { + warnings["generate-types-for-anonymous-schemas"] = "the flag is set with `generate.models: false` and a client/server generator. The hoisted named types this config emits references to will not be declared by this config. If a sibling config emits `generate.models: true` into the same Go package with the same flag setting, you can ignore this warning. Otherwise, set `generate.models: true` in this config or remove the flag to fall back to anonymous structs." + } + + return warnings +} + // UpdateDefaults sets reasonable default values for unset fields in Configuration func (o Configuration) UpdateDefaults() Configuration { if reflect.ValueOf(o.Generate).IsZero() { @@ -110,8 +165,12 @@ type GenerateOptions struct { ChiServer bool `yaml:"chi-server,omitempty"` // FiberServer specifies whether to generate fiber server boilerplate FiberServer bool `yaml:"fiber-server,omitempty"` + // FiberV3Server specifies whether to generate fiber v3 server boilerplate + FiberV3Server bool `yaml:"fiber-v3-server,omitempty"` // EchoServer specifies whether to generate echo server boilerplate EchoServer bool `yaml:"echo-server,omitempty"` + // Echo5Server specifies whether to generate echo v5 server boilerplate + Echo5Server bool `yaml:"echo5-server,omitempty"` // GinServer specifies whether to generate gin server boilerplate GinServer bool `yaml:"gin-server,omitempty"` // GorillaServer specifies whether to generate Gorilla server boilerplate @@ -130,6 +189,45 @@ type GenerateOptions struct { ServerURLs bool `yaml:"server-urls,omitempty"` } +// RouterImports returns the framework-specific and strict middleware imports +// needed based on which server type is selected. +func (g GenerateOptions) RouterImports() []AdditionalImport { + var imports []AdditionalImport + + switch { + case g.EchoServer: + imports = append(imports, AdditionalImport{Package: "github.com/labstack/echo/v4"}) + case g.Echo5Server: + imports = append(imports, AdditionalImport{Package: "github.com/labstack/echo/v5"}) + case g.ChiServer: + imports = append(imports, AdditionalImport{Package: "github.com/go-chi/chi/v5"}) + case g.GinServer: + imports = append(imports, AdditionalImport{Package: "github.com/gin-gonic/gin"}) + case g.GorillaServer: + imports = append(imports, AdditionalImport{Package: "github.com/gorilla/mux"}) + case g.FiberServer: + imports = append(imports, AdditionalImport{Package: "github.com/gofiber/fiber/v2"}) + case g.FiberV3Server: + imports = append(imports, AdditionalImport{Package: "github.com/gofiber/fiber/v3"}) + case g.IrisServer: + imports = append(imports, AdditionalImport{Package: "github.com/kataras/iris/v12"}) + imports = append(imports, AdditionalImport{Package: "github.com/kataras/iris/v12/core/router"}) + case g.StdHTTPServer: + } + + return imports +} + +// AnyOperationGenerator returns true if any code generator that emits +// per-operation Go code (clients, server frameworks, strict-server) is +// enabled. Used to detect configurations where operation-derived type +// references would be emitted without a corresponding declaration. +func (g GenerateOptions) AnyOperationGenerator() bool { + return g.Client || g.IrisServer || g.EchoServer || g.Echo5Server || + g.ChiServer || g.FiberServer || g.FiberV3Server || g.GinServer || g.GorillaServer || + g.StdHTTPServer || g.Strict +} + func (oo GenerateOptions) Validate() map[string]string { return nil } @@ -173,6 +271,19 @@ type CompatibilityOptions struct { // than we have in the past. Set OldMergeSchemas to true for the old behavior. // Please see https://github.com/oapi-codegen/oapi-codegen/issues/531 OldMergeSchemas bool `yaml:"old-merge-schemas,omitempty"` + // In the past, when a schema combined `allOf` with sibling fields at the + // same level (`properties`, `required`, `additionalProperties`, + // `description`), those siblings were silently discarded and the schema + // was emitted as a Go type alias to its sole `allOf` target. New behavior + // merges the parent's siblings with the `allOf` members so the generated + // type carries every field declared in the spec. This is a more accurate + // translation of OpenAPI semantics, but it changes the shape of generated + // types: a schema that previously produced `type X = Y` may now produce + // a distinct struct embedding Y with extra fields, which is not + // interchangeable with Y in downstream Go code. Set OldAllOfSiblingMerging + // to true to restore the prior behavior. + // Please see https://github.com/oapi-codegen/oapi-codegen/issues/697 + OldAllOfSiblingMerging bool `yaml:"old-allof-sibling-merging,omitempty"` // Enum values can generate conflicting typenames, so we've updated the // code for enum generation to avoid these conflicts, but it will result // in some enum types being renamed in existing code. Set OldEnumConflicts to true @@ -195,6 +306,14 @@ type CompatibilityOptions struct { // When set to true, always prefix enum values with their type name instead of only // when typenames would be conflicting. AlwaysPrefixEnumValues bool `yaml:"always-prefix-enum-values,omitempty"` + // DisableEnumValueConflictResolution turns off cross-enum value conflict + // resolution. By default, when two enums declare the same value, the + // generated constants are prefixed with their type name so they don't + // collide. Set this to true to disable that resolution and restore the + // prior behavior, in which conflict detection compared already-prefixed + // constant names and could miss overlaps depending on declaration order. + // Please see https://github.com/oapi-codegen/oapi-codegen/issues/2391 + DisableEnumValueConflictResolution bool `yaml:"disable-enum-value-conflict-resolution,omitempty"` // Our generated code for Chi has historically inverted the order in which Chi middleware is // applied such that the last invoked middleware ends up executing first in the Chi chain // This resolves the behavior such that middlewares are chained in the order they are invoked. @@ -234,6 +353,49 @@ type CompatibilityOptions struct { // NOTE that this will not impact generated code. // NOTE that if you're using `include-operation-ids` or `exclude-operation-ids` you may want to ensure that the `operationId`s used are correct. PreserveOriginalOperationIdCasingInEmbeddedSpec bool `yaml:"preserve-original-operation-id-casing-in-embedded-spec"` + + // HeadersImplicitlyRequired treats all response headers as required, ignoring + // the `required` property from the header definition. Prior to v2.6.0, + // oapi-codegen generated all response headers as direct values (implicitly + // required). The OpenAPI specification defaults headers to optional + // (required: false), so the corrected behavior generates optional headers as + // pointers. Set this to true to restore the old behavior where all headers + // are treated as required. + // Please see https://github.com/oapi-codegen/oapi-codegen/issues/2267 + HeadersImplicitlyRequired bool `yaml:"headers-implicitly-required,omitempty"` + + // EnableAuthScopesOnContext re-enables the legacy emission of security + // scheme scopes by generated server code: the per-scheme context key + // types (e.g. `bearerAuthContextKey`), the scope constants (e.g. + // `BearerAuthScopes`), and the per-operation calls that store the + // operation's scopes into the request context. + // + // A security scheme that is a $ref into a spec covered by import-mapping + // does not declare its own context key type; its scopes constant is an + // alias of the one in the mapped package, so `context.Value` lookups use + // the same key across the generated packages. This requires the mapped + // spec's config to also set this flag, so that the referenced constant + // exists. Please see + // https://github.com/oapi-codegen/oapi-codegen/issues/2383 + // + // Deprecated: this mechanism is off by default because it flattens the + // OpenAPI `security` requirements into a per-scheme list of scopes, and + // cannot represent alternative schemes (OR), combined schemes (AND), or + // anonymous (`{}`) alternatives. Authentication and authorization should + // instead be performed at runtime using the request validation + // middleware, which evaluates the spec's security requirements directly. + // Please see https://github.com/oapi-codegen/oapi-codegen/issues/1524 + EnableAuthScopesOnContext bool `yaml:"enable-auth-scopes-on-context,omitempty"` + + // SortHandlerRegistrations restores the historical behavior of registering + // generated route handlers in sorted (lexicographic, by path then method) + // order. By default handlers are registered in the order their paths are + // declared in the spec, so that on routers which match in registration + // order (e.g. Fiber, Gorilla/mux) overlapping paths can be disambiguated by + // ordering them in the spec. Set this to true to opt out and go back to the + // old sorted registration order. + // Please see https://github.com/oapi-codegen/oapi-codegen/issues/1887 + SortHandlerRegistrations bool `yaml:"sort-handler-registrations,omitempty"` } func (co CompatibilityOptions) Validate() map[string]string { @@ -246,6 +408,18 @@ type OutputOptions struct { SkipFmt bool `yaml:"skip-fmt,omitempty"` // Whether to skip pruning unused components on the generated code SkipPrune bool `yaml:"skip-prune,omitempty"` + // SkipEnumValidate disables the generation of the `Valid()` method on + // enum types. By default each generated enum type includes a `Valid() + // bool` method which returns true when the value matches one of the + // defined constants; set this to true to suppress that method when it + // conflicts with user-defined methods of the same name. + SkipEnumValidate bool `yaml:"skip-enum-validate,omitempty"` + // SkipEnumViaOneOf disables detection of the OpenAPI 3.1 enum-via-oneOf + // idiom: a schema with `type: string|integer` and `oneOf:` members that + // each carry `const` + `title` will normally be emitted as a Go enum with + // named constants. Set this to true to fall through to the standard union + // generator instead. + SkipEnumViaOneOf bool `yaml:"skip-enum-via-oneof,omitempty"` // Only include operations that have one of these tags. Ignored when empty. IncludeTags []string `yaml:"include-tags,omitempty"` // Exclude operations that have one of these tags. Ignored when empty. @@ -263,11 +437,15 @@ type OutputOptions struct { ResponseTypeSuffix string `yaml:"response-type-suffix,omitempty"` // Override the default generated client type with the value ClientTypeName string `yaml:"client-type-name,omitempty"` - // Whether to use the initialism overrides - InitialismOverrides bool `yaml:"initialism-overrides,omitempty"` // AdditionalInitialisms is a list of additional initialisms to use when generating names. // NOTE that this has no effect unless the `name-normalizer` is set to `ToCamelCaseWithInitialisms` AdditionalInitialisms []string `yaml:"additional-initialisms,omitempty"` + // StreamingContentTypes are regex patterns matched against response + // Content-Type to decide when to generate a flush-per-chunk streaming + // response path. User-provided patterns are merged with the defaults + // (text/event-stream, application/jsonl, application/x-ndjson); invalid + // regexes fail Validate(). + StreamingContentTypes []string `yaml:"streaming-content-types,omitempty"` // Whether to generate nullable type for nullable fields NullableType bool `yaml:"nullable-type,omitempty"` @@ -276,6 +454,9 @@ type OutputOptions struct { // "array" DisableTypeAliasesForType []string `yaml:"disable-type-aliases-for-type"` + // SkipResponseBodyGetters decides whether to generate getter methods for response bodies. + SkipResponseBodyGetters bool `yaml:"skip-response-body-getters,omitempty"` + // NameNormalizer is the method used to normalize Go names and types, for instance converting the text `MyApi` to `MyAPI`. Corresponds with the constants defined for `codegen.NameNormalizerFunction` NameNormalizer string `yaml:"name-normalizer,omitempty"` @@ -288,6 +469,9 @@ type OutputOptions struct { // ClientResponseBytesFunction decides whether to enable the generation of a `Bytes()` method on response objects for `ClientWithResponses` ClientResponseBytesFunction bool `yaml:"client-response-bytes-function,omitempty"` + // SkipClientResponseContentType disables the generation of a `ContentType()` method on response objects for `ClientWithResponses`, which is otherwise generated by default. + SkipClientResponseContentType bool `yaml:"skip-client-response-content-type,omitempty"` + // PreferSkipOptionalPointer allows defining at a global level whether to omit the pointer for a type to indicate that the field/type is optional. // This is the same as adding `x-go-type-skip-optional-pointer` to each field (manually, or using an OpenAPI Overlay) PreferSkipOptionalPointer bool `yaml:"prefer-skip-optional-pointer,omitempty"` @@ -300,6 +484,46 @@ type OutputOptions struct { // PreferSkipOptionalPointerOnContainerTypes allows disabling the generation of an "optional pointer" for an optional field that is a container type (such as a slice or a map), which ends up requiring an additional, unnecessary, `... != nil` check PreferSkipOptionalPointerOnContainerTypes bool `yaml:"prefer-skip-optional-pointer-on-container-types,omitempty"` + + // ResolveTypeNameCollisions, when set to true, automatically renames + // types that collide across different OpenAPI component sections + // (schemas, parameters, requestBodies, responses, headers) by appending + // a suffix based on the component section (e.g., "Parameter", "Response", + // "RequestBody"). It also detects collisions between component types and + // client response wrapper types (e.g., issue #1474). Without this, the + // codegen will error on duplicate type names, requiring manual resolution + // via x-go-name. + ResolveTypeNameCollisions bool `yaml:"resolve-type-name-collisions,omitempty"` + + // GenerateTypesForAnonymousSchemas, when true, causes oapi-codegen to + // emit a named Go type for every inline schema that would otherwise + // generate as an anonymous `struct { ... }`. The type's name is derived + // from the schema path (e.g. `GetRolesIdResponseBody_Data`). Default + // false. Equivalent to adding `x-go-type-name` to every inline schema; + // when both are present at the same site, `x-go-type-name` wins. + // + // The hoisted named types are declared by the same emission path that + // `generate.models` controls. In a single-config setup, this flag is + // only effective when `generate.models: true` is also set in the same + // config — otherwise the generated client/server code will reference + // type names that no emission path declares, and `go build` will fail. + // + // In a multi-config setup where one config emits `models` and a + // sibling config emits a client or server framework into the same + // Go package, the flag must be set consistently across all configs. + // Mixing the flag (set in one config, omitted in the other) causes + // references and declarations to disagree on whether to use the + // hoisted name or the inline struct. The sibling config that does + // not emit `models` will produce a warning at codegen time noting + // that it does not declare the hoisted names; you can ignore the + // warning when the sibling models config will declare them. + // + // See https://github.com/oapi-codegen/oapi-codegen/issues/1139 + GenerateTypesForAnonymousSchemas bool `yaml:"generate-types-for-anonymous-schemas,omitempty"` + + // TypeMapping allows customizing OpenAPI type/format to Go type mappings. + // User-specified mappings are merged on top of the defaults. + TypeMapping *TypeMapping `yaml:"type-mapping,omitempty"` } func (oo OutputOptions) Validate() map[string]string { @@ -309,9 +533,33 @@ func (oo OutputOptions) Validate() map[string]string { } } + if _, err := compileStreamingContentTypes(oo.StreamingContentTypes); err != nil { + return map[string]string{ + "streaming-content-types": err.Error(), + } + } + return nil } +// compileStreamingContentTypes returns the merged default + user-provided +// patterns compiled into regexes. The first compile error is returned +// including the offending pattern. +func compileStreamingContentTypes(user []string) ([]*regexp.Regexp, error) { + all := make([]string, 0, len(defaultStreamingContentTypes)+len(user)) + all = append(all, defaultStreamingContentTypes...) + all = append(all, user...) + out := make([]*regexp.Regexp, 0, len(all)) + for _, p := range all { + re, err := regexp.Compile(p) + if err != nil { + return nil, fmt.Errorf("invalid streaming-content-type pattern %q: %w", p, err) + } + out = append(out, re) + } + return out, nil +} + type OutputOptionsOverlay struct { Path string `yaml:"path"` diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/extension.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/extension.go index 579d8a17c..dbf6611f4 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/extension.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/extension.go @@ -5,7 +5,10 @@ import ( ) const ( - // extPropGoType overrides the generated type definition. + // extPropGoType overrides the generated type definition. When + // resolve-type-name-collisions is enabled, the collision resolver + // controls the final Go type name; this extension controls what + // that name aliases or refers to. extPropGoType = "x-go-type" // extPropGoTypeSkipOptionalPointer specifies that optional fields should // be the type itself instead of a pointer to the type. @@ -14,7 +17,10 @@ const ( extPropGoImport = "x-go-type-import" // extGoName is used to override a field name extGoName = "x-go-name" - // extGoTypeName is used to override a generated typename for something. + // extGoTypeName overrides a generated typename. When + // resolve-type-name-collisions is enabled, the collision resolver + // controls the top-level Go type name; this extension controls + // the name of the underlying type definition that gets aliased. extGoTypeName = "x-go-type-name" extPropGoJsonIgnore = "x-go-json-ignore" extPropOmitEmpty = "x-omitempty" @@ -29,7 +35,7 @@ const ( extOapiCodegenOnlyHonourGoName = "x-oapi-codegen-only-honour-go-name" ) -func extString(extPropValue interface{}) (string, error) { +func extString(extPropValue any) (string, error) { str, ok := extPropValue.(string) if !ok { return "", fmt.Errorf("failed to convert type: %T", extPropValue) @@ -37,11 +43,11 @@ func extString(extPropValue interface{}) (string, error) { return str, nil } -func extTypeName(extPropValue interface{}) (string, error) { +func extTypeName(extPropValue any) (string, error) { return extString(extPropValue) } -func extParsePropGoTypeSkipOptionalPointer(extPropValue interface{}) (bool, error) { +func extParsePropGoTypeSkipOptionalPointer(extPropValue any) (bool, error) { goTypeSkipOptionalPointer, ok := extPropValue.(bool) if !ok { return false, fmt.Errorf("failed to convert type: %T", extPropValue) @@ -49,11 +55,11 @@ func extParsePropGoTypeSkipOptionalPointer(extPropValue interface{}) (bool, erro return goTypeSkipOptionalPointer, nil } -func extParseGoFieldName(extPropValue interface{}) (string, error) { +func extParseGoFieldName(extPropValue any) (string, error) { return extString(extPropValue) } -func extParseOmitEmpty(extPropValue interface{}) (bool, error) { +func extParseOmitEmpty(extPropValue any) (bool, error) { omitEmpty, ok := extPropValue.(bool) if !ok { return false, fmt.Errorf("failed to convert type: %T", extPropValue) @@ -61,7 +67,7 @@ func extParseOmitEmpty(extPropValue interface{}) (bool, error) { return omitEmpty, nil } -func extParseOmitZero(extPropValue interface{}) (bool, error) { +func extParseOmitZero(extPropValue any) (bool, error) { omitZero, ok := extPropValue.(bool) if !ok { return false, fmt.Errorf("failed to convert type: %T", extPropValue) @@ -69,8 +75,8 @@ func extParseOmitZero(extPropValue interface{}) (bool, error) { return omitZero, nil } -func extExtraTags(extPropValue interface{}) (map[string]string, error) { - tagsI, ok := extPropValue.(map[string]interface{}) +func extExtraTags(extPropValue any) (map[string]string, error) { + tagsI, ok := extPropValue.(map[string]any) if !ok { return nil, fmt.Errorf("failed to convert type: %T", extPropValue) } @@ -85,7 +91,7 @@ func extExtraTags(extPropValue interface{}) (map[string]string, error) { return tags, nil } -func extParseGoJsonIgnore(extPropValue interface{}) (bool, error) { +func extParseGoJsonIgnore(extPropValue any) (bool, error) { goJsonIgnore, ok := extPropValue.(bool) if !ok { return false, fmt.Errorf("failed to convert type: %T", extPropValue) @@ -93,8 +99,8 @@ func extParseGoJsonIgnore(extPropValue interface{}) (bool, error) { return goJsonIgnore, nil } -func extParseEnumVarNames(extPropValue interface{}) ([]string, error) { - namesI, ok := extPropValue.([]interface{}) +func extParseEnumVarNames(extPropValue any) ([]string, error) { + namesI, ok := extPropValue.([]any) if !ok { return nil, fmt.Errorf("failed to convert type: %T", extPropValue) } @@ -109,11 +115,11 @@ func extParseEnumVarNames(extPropValue interface{}) ([]string, error) { return names, nil } -func extParseDeprecationReason(extPropValue interface{}) (string, error) { +func extParseDeprecationReason(extPropValue any) (string, error) { return extString(extPropValue) } -func extParseOapiCodegenOnlyHonourGoName(extPropValue interface{}) (bool, error) { +func extParseOapiCodegenOnlyHonourGoName(extPropValue any) (bool, error) { onlyHonourGoName, ok := extPropValue.(bool) if !ok { return false, fmt.Errorf("failed to convert type: %T", extPropValue) diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/externalref.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/externalref.go index a48b3d4e1..829885a5b 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/externalref.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/externalref.go @@ -58,6 +58,20 @@ func ensureExternalRefsInParameterDefinitions(defs *[]ParameterDefinition, ref s } } +// externalPackageFor returns the imported Go package name for a `$ref` that +// targets a file outside the current spec. Returns an empty string when the +// ref is empty, points within the current spec, or has no import-mapping. +func externalPackageFor(ref string) string { + if ref == "" { + return "" + } + parts := strings.SplitN(ref, "#", 2) + if pack, ok := globalState.importMapping[parts[0]]; ok { + return pack.Name + } + return "" +} + // ensureExternalRefsInSchema ensures that when an externalRef (`$ref` that points to a file that isn't the current spec) is encountered, we make sure we update our underlying `RefType` to make sure that we point to that type. // // This only happens if we have a non-empty `ref` passed in, and that `ref` isn't pointing to something in our file @@ -68,13 +82,26 @@ func ensureExternalRefsInSchema(schema *Schema, ref string) { return } - // if this is already defined as the start of a struct, we shouldn't inject **??** - if strings.HasPrefix(schema.GoType, "struct {") { + parts := strings.SplitN(ref, "#", 2) + pack, ok := globalState.importMapping[parts[0]] + if !ok { return } - parts := strings.SplitN(ref, "#", 2) - if pack, ok := globalState.importMapping[parts[0]]; ok { + // if this is already defined as the start of a struct, we shouldn't inject **??** + if !strings.HasPrefix(schema.GoType, "struct {") { schema.RefType = fmt.Sprintf("%s.%s", pack.Name, schema.GoType) } + + // Qualify union branch types. Each UnionElement was resolved as a + // local reference during generateUnion (e.g. "Bionicle"); when the + // enclosing schema came from an externally-ref'd file the branches + // actually live in the imported package, so the As/From/Merge + // methods generated by union.tmpl must use ".Bionicle" instead. + for i, elem := range schema.UnionElements { + s := string(elem) + if !strings.Contains(s, ".") { + schema.UnionElements[i] = UnionElement(fmt.Sprintf("%s.%s", pack.Name, s)) + } + } } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/gather.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/gather.go new file mode 100644 index 000000000..0daf2ccf7 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/gather.go @@ -0,0 +1,338 @@ +package codegen + +import ( + "cmp" + "fmt" + "slices" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + + "github.com/oapi-codegen/oapi-codegen/v2/pkg/util" +) + +var _ fmt.Stringer = (*SchemaPath)(nil) + +// SchemaPath represents the document location of a schema, e.g. +// ["components", "schemas", "Pet", "properties", "name"]. +type SchemaPath []string + +// String returns the path joined with "/". +func (sp SchemaPath) String() string { + return strings.Join(sp, "/") +} + +var _ fmt.Stringer = (*SchemaContext)(nil) + +// SchemaContext identifies where in the OpenAPI document a schema was found. +type SchemaContext int + +const ( + ContextComponentSchema SchemaContext = iota + ContextComponentParameter + ContextComponentRequestBody + ContextComponentResponse + ContextComponentHeader + ContextOperationParameter + ContextOperationRequestBody + ContextOperationResponse + ContextClientResponseWrapper +) + +// String returns a human-readable name for the context. +func (sc SchemaContext) String() string { + switch sc { + case ContextComponentSchema: + return "Schema" + case ContextComponentParameter: + return "Parameter" + case ContextComponentRequestBody: + return "RequestBody" + case ContextComponentResponse: + return "Response" + case ContextComponentHeader: + return "Header" + case ContextOperationParameter: + return "OperationParameter" + case ContextOperationRequestBody: + return "OperationRequestBody" + case ContextOperationResponse: + return "OperationResponse" + case ContextClientResponseWrapper: + return "ClientResponseWrapper" + default: + return "Unknown" + } +} + +// Suffix returns the suffix to use for collision resolution. +func (sc SchemaContext) Suffix() string { + switch sc { + case ContextComponentSchema: + return "Schema" + case ContextComponentParameter, ContextOperationParameter: + return "Parameter" + case ContextComponentRequestBody, ContextOperationRequestBody: + return "RequestBody" + case ContextComponentResponse, ContextOperationResponse: + return "Response" + case ContextComponentHeader: + return "Header" + case ContextClientResponseWrapper: + return "Response" + default: + return "" + } +} + +// GatheredSchema represents a schema discovered during the gather pass, +// along with its document location and context metadata. +type GatheredSchema struct { + Path SchemaPath + Context SchemaContext + Ref string // $ref string if this is a reference + Schema *openapi3.Schema // The resolved schema value + OperationID string // Enclosing operation's ID, if any + ContentType string // Media type, if from request/response body + StatusCode string // HTTP status code, if from a response + ParamIndex int // Parameter index within an operation + ComponentName string // The component name (e.g., "Bar" for components/schemas/Bar) + GoNameOverride string // x-go-name override from the component or its parent container +} + +// IsComponentSchema returns true if this schema came from components/schemas. +func (gs *GatheredSchema) IsComponentSchema() bool { + return gs.Context == ContextComponentSchema +} + +// GatherSchemas walks the entire OpenAPI spec and collects all schemas that +// will need Go type names. This is the first pass of the multi-pass resolution. +func GatherSchemas(spec *openapi3.T, opts Configuration) []*GatheredSchema { + var schemas []*GatheredSchema + + if spec.Components != nil { + schemas = slices.Concat( + gatherComponentSchemas(spec.Components), + gatherComponentParameters(spec.Components), + gatherComponentResponses(spec.Components), + gatherComponentRequestBodies(spec.Components), + gatherComponentHeaders(spec.Components), + ) + } + + // Gather client response wrapper types for operations that will generate + // client code. These synthetic entries exist so wrapper types like + // `CreateChatCompletionResponse` participate in collision detection. + if opts.Generate.Client { + schemas = append(schemas, gatherClientResponseWrappers(spec)...) + } + + return schemas +} + +func gatherComponentSchemas(components *openapi3.Components) []*GatheredSchema { + var result []*GatheredSchema + for _, name := range SortedSchemaKeys(components.Schemas) { + schemaRef := components.Schemas[name] + if schemaRef == nil || schemaRef.Value == nil { + continue + } + var goNameOverride string + if schemaRef.Ref == "" { + goNameOverride = extractGoNameOverride(schemaRef.Value.Extensions) + } + result = append(result, &GatheredSchema{ + Path: SchemaPath{"components", "schemas", name}, + Context: ContextComponentSchema, + Ref: schemaRef.Ref, + Schema: schemaRef.Value, + ComponentName: name, + GoNameOverride: goNameOverride, + }) + } + return result +} + +func gatherComponentParameters(components *openapi3.Components) []*GatheredSchema { + var result []*GatheredSchema + for _, name := range SortedMapKeys(components.Parameters) { + paramRef := components.Parameters[name] + if paramRef == nil || paramRef.Value == nil { + continue + } + param := paramRef.Value + if param.Schema != nil && param.Schema.Value != nil { + var goNameOverride string + if paramRef.Ref == "" { + goNameOverride = extractGoNameOverride(param.Extensions) + } + result = append(result, &GatheredSchema{ + Path: SchemaPath{"components", "parameters", name}, + Context: ContextComponentParameter, + Ref: paramRef.Ref, + Schema: param.Schema.Value, + ComponentName: name, + GoNameOverride: goNameOverride, + }) + } + } + return result +} + +func gatherComponentResponses(components *openapi3.Components) []*GatheredSchema { + var result []*GatheredSchema + for _, name := range SortedMapKeys(components.Responses) { + responseRef := components.Responses[name] + if responseRef == nil || responseRef.Value == nil { + continue + } + response := responseRef.Value + var goNameOverride string + if responseRef.Ref == "" { + goNameOverride = extractGoNameOverride(response.Extensions) + } + for _, mediaType := range SortedMapKeys(response.Content) { + if !util.IsMediaTypeJson(mediaType) { + continue + } + mt := response.Content[mediaType] + if mt.Schema != nil && mt.Schema.Value != nil { + result = append(result, &GatheredSchema{ + Path: SchemaPath{"components", "responses", name, "content", mediaType}, + Context: ContextComponentResponse, + Ref: responseRef.Ref, + Schema: mt.Schema.Value, + ContentType: mediaType, + ComponentName: name, + GoNameOverride: goNameOverride, + }) + } + } + } + return result +} + +func gatherComponentRequestBodies(components *openapi3.Components) []*GatheredSchema { + var result []*GatheredSchema + for _, name := range SortedMapKeys(components.RequestBodies) { + bodyRef := components.RequestBodies[name] + if bodyRef == nil || bodyRef.Value == nil { + continue + } + body := bodyRef.Value + var goNameOverride string + if bodyRef.Ref == "" { + goNameOverride = extractGoNameOverride(body.Extensions) + } + for _, mediaType := range SortedMapKeys(body.Content) { + if !util.IsMediaTypeJson(mediaType) { + continue + } + mt := body.Content[mediaType] + if mt.Schema != nil && mt.Schema.Value != nil { + result = append(result, &GatheredSchema{ + Path: SchemaPath{"components", "requestBodies", name, "content", mediaType}, + Context: ContextComponentRequestBody, + Ref: bodyRef.Ref, + Schema: mt.Schema.Value, + ContentType: mediaType, + ComponentName: name, + GoNameOverride: goNameOverride, + }) + } + } + } + return result +} + +func gatherComponentHeaders(components *openapi3.Components) []*GatheredSchema { + var result []*GatheredSchema + for _, name := range SortedMapKeys(components.Headers) { + headerRef := components.Headers[name] + if headerRef == nil || headerRef.Value == nil { + continue + } + header := headerRef.Value + if header.Schema != nil && header.Schema.Value != nil { + var goNameOverride string + if headerRef.Ref == "" { + goNameOverride = extractGoNameOverride(header.Extensions) + } + result = append(result, &GatheredSchema{ + Path: SchemaPath{"components", "headers", name}, + Context: ContextComponentHeader, + Ref: headerRef.Ref, + Schema: header.Schema.Value, + ComponentName: name, + GoNameOverride: goNameOverride, + }) + } + } + return result +} + +// gatherClientResponseWrappers creates synthetic schema entries for each +// operation that would generate a client response wrapper type like +// `Response`. These don't correspond to a real schema in the +// spec but they need names that don't collide with real types. +func gatherClientResponseWrappers(spec *openapi3.T) []*GatheredSchema { + if spec.Paths == nil { + return nil + } + + // Collect all operations sorted for determinism + type opEntry struct { + path string + method string + op *openapi3.Operation + } + var ops []opEntry + + pathKeys := SortedMapKeys(spec.Paths.Map()) + for _, path := range pathKeys { + pathItem := spec.Paths.Find(path) + if pathItem == nil { + continue + } + for method, op := range pathItem.Operations() { + if op != nil && op.OperationID != "" { + ops = append(ops, opEntry{path: path, method: method, op: op}) + } + } + } + + // Sort by operationID for determinism + slices.SortFunc(ops, func(a, b opEntry) int { + return cmp.Compare(a.op.OperationID, b.op.OperationID) + }) + + result := make([]*GatheredSchema, 0, len(ops)) + for _, entry := range ops { + result = append(result, &GatheredSchema{ + Path: SchemaPath{"paths", entry.path, entry.method, "x-client-response-wrapper"}, + Context: ContextClientResponseWrapper, + OperationID: entry.op.OperationID, + }) + } + + return result +} + +// FormatPath returns a human-readable representation of the path for debugging. +func (gs *GatheredSchema) FormatPath() string { + return fmt.Sprintf("#/%s", strings.Join(gs.Path, "/")) +} + +// extractGoNameOverride reads the x-go-name extension from extensions and +// returns its value, or "" if not present or invalid. +func extractGoNameOverride(extensions map[string]any) string { + ext, ok := extensions[extGoName] + if !ok { + return "" + } + name, err := extTypeName(ext) + if err != nil { + return "" + } + return name +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/inline.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/inline.go index 459255c05..c07e1fe65 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/inline.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/inline.go @@ -15,7 +15,7 @@ package codegen import ( "bytes" - "compress/gzip" + "compress/flate" "context" "encoding/base64" "fmt" @@ -35,20 +35,21 @@ func GenerateInlinedSpec(t *template.Template, importMapping importMap, swagger return "", fmt.Errorf("error marshaling swagger: %w", err) } - // gzip + // flate var buf bytes.Buffer - zw, err := gzip.NewWriterLevel(&buf, gzip.BestCompression) + zw, err := flate.NewWriter(&buf, flate.BestCompression) if err != nil { - return "", fmt.Errorf("error creating gzip compressor: %w", err) + return "", fmt.Errorf("new flate writer: %w", err) } - _, err = zw.Write(encoded) - if err != nil { - return "", fmt.Errorf("error gzipping swagger file: %w", err) + + if _, err := zw.Write(encoded); err != nil { + return "", fmt.Errorf("write flate: %w", err) } - err = zw.Close() - if err != nil { - return "", fmt.Errorf("error gzipping swagger file: %w", err) + + if err := zw.Close(); err != nil { + return "", fmt.Errorf("close flate writer: %w", err) } + str := base64.StdEncoding.EncodeToString(buf.Bytes()) var parts []string diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/merge_schemas.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/merge_schemas.go index 04e7b2fa2..0585044a0 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/merge_schemas.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/merge_schemas.go @@ -3,6 +3,8 @@ package codegen import ( "errors" "fmt" + "maps" + "reflect" "strings" "github.com/getkin/kin-openapi/openapi3" @@ -26,30 +28,138 @@ func mergeSchemas(allOf []*openapi3.SchemaRef, path []string) (Schema, error) { return GenerateGoSchema(allOf[0], path) } + // Distinguish two uses of allOf: + // + // 1. Decorator idiom — at least one INLINE member (Ref == "") is + // "extension-only" (carries no structural content). This is a + // workaround for OpenAPI 3.0's $ref-sibling restriction: users + // wrap a $ref in allOf to attach extensions like + // x-go-type-skip-optional-pointer (see issue #1957). Here + // extensions are meant to flow through to the result. + // + // 2. Real composition — every member either contributes structural + // content or is a $ref contributing the referenced schema. The + // result is a NEW distinct type, and extensions like x-go-type on + // a source schema do NOT transfer (see issue #2335: Client has + // x-go-type=OverlayClient, but allOf[Client, {properties:{id}}] + // is ClientWithId — a different shape, not OverlayClient). + // + // A $ref member is excluded from the decorator check because it is by + // construction delivering the referenced schema, not "decorating" + // siblings — even if the referenced schema happens to carry only + // extensions, that's a property of the target, not an intent on this + // composition. + decoratorIdiom := false + for _, m := range allOf { + if m.Ref == "" && isExtensionOnlySchema(m.Value) { + decoratorIdiom = true + break + } + } + schema, err := valueWithPropagatedRef(allOf[0]) if err != nil { return Schema{}, err } + // Seed allOf[0]'s ref so that if s1's own AllOf contains a back-reference + // to itself, the cycle is detected during recursive merging. + seenTopLevel := make(map[string]bool) + if allOf[0].Ref != "" { + seenTopLevel[allOf[0].Ref] = true + } + for i := 1; i < n; i++ { var err error oneOfSchema, err := valueWithPropagatedRef(allOf[i]) if err != nil { return Schema{}, err } - schema, err = mergeOpenapiSchemas(schema, oneOfSchema, true) + + seenSchemaRef := make(map[string]bool) + for k := range seenTopLevel { + seenSchemaRef[k] = true + } + if allOf[i].Ref != "" { + seenSchemaRef[allOf[i].Ref] = true + seenTopLevel[allOf[i].Ref] = true + } + schema, err = mergeOpenapiSchemas(schema, oneOfSchema, true, seenSchemaRef) if err != nil { return Schema{}, fmt.Errorf("error merging schemas for AllOf: %w", err) } } + + if !decoratorIdiom { + // Drop only the type-identity directives. Other extensions + // (user-defined x-* metadata, etc.) are preserved — we only + // have concrete evidence that the identity-bound ones cause + // incorrect aliasing across composition. + // + // Clone before mutating: the current merge path always + // reallocates schema.Extensions in mergeOpenapiSchemas before + // we reach here, so the delete is safe today — but the + // defensive copy keeps this correct if that invariant changes + // (e.g. an allocation-skipping optimization). Cost is a small + // map copy on a single code path. + ext := maps.Clone(schema.Extensions) + delete(ext, extPropGoType) + delete(ext, extGoTypeName) + delete(ext, extPropGoImport) + schema.Extensions = ext + } + return GenerateGoSchema(openapi3.NewSchemaRef("", &schema), path) } -// valueWithPropagatedRef returns a copy of ref schema with its Properties refs -// updated if ref itself is external. Otherwise, return ref.Value as-is. +// isExtensionOnlySchema reports whether a schema carries only extensions, +// with no structural or constraint-bearing content. Used to detect the +// "$ref + sibling extension" idiom: allOf wrappers whose purpose is +// attaching extensions to a $ref (since OpenAPI 3.0 disallows sibling +// keys next to $ref). +// +// Implementation: zero out every field that doesn't affect the generated +// Go type, then compare to the zero Schema. Anything left over — a Type, +// Properties, Pattern, MinLength, etc. — disqualifies the schema from +// being treated as a pure decorator. This formulation defaults to safe +// behavior if kin-openapi gains new structural fields: they'd be non-zero +// by default and correctly disqualify. +func isExtensionOnlySchema(s *openapi3.Schema) bool { + if s == nil || len(s.Extensions) == 0 { + return false + } + tmp := *s + tmp.Extensions = nil + // Source-tracking metadata from kin-openapi; always non-nil for + // schemas parsed from a file. + tmp.Origin = nil + // Purely documentary / metadata fields. These don't affect the + // generated Go type, so a schema carrying only these plus extensions + // still behaves as a decorator. + tmp.Title = "" + tmp.Description = "" + tmp.Default = nil + tmp.Example = nil + tmp.ExternalDocs = nil + tmp.Deprecated = false + tmp.ReadOnly = false + tmp.WriteOnly = false + tmp.AllowEmptyValue = false + tmp.XML = nil + return reflect.DeepEqual(tmp, openapi3.Schema{}) +} + +// valueWithPropagatedRef returns a copy of ref's schema with its Properties +// refs rewritten when ref itself is external, and with extensions placed +// next to the $ref folded in (ref-side wins over value-side). This is what +// allows allOf members to carry per-use sibling directives without +// mutating the referenced schema. func valueWithPropagatedRef(ref *openapi3.SchemaRef) (openapi3.Schema, error) { + schema := *ref.Value + schema.Extensions = combinedSchemaExtensions(ref) + if len(ref.Ref) == 0 || ref.Ref[0] == '#' { - return *ref.Value, nil + return schema, nil } pathParts := strings.Split(ref.Ref, "#") @@ -58,23 +168,79 @@ func valueWithPropagatedRef(ref *openapi3.SchemaRef) (openapi3.Schema, error) { } remoteComponent := pathParts[0] - // remote ref - schema := *ref.Value + propagateRemoteRefs(remoteComponent, &schema) + + return schema, nil +} + +// propagateRemoteRefs rewrites local "#/..." refs within a schema to be +// qualified with the remote component path. This is needed so that when an +// external schema is flattened via allOf, nested type references (array items, +// additionalProperties, sub-object properties) retain their external +// qualification. See https://github.com/oapi-codegen/oapi-codegen/issues/2288 +func propagateRemoteRefs(remoteComponent string, schema *openapi3.Schema) { for _, value := range schema.Properties { if len(value.Ref) > 0 && value.Ref[0] == '#' { - // local reference, should propagate remote value.Ref = remoteComponent + value.Ref + } else if value.Value != nil { + propagateRemoteRefs(remoteComponent, value.Value) } } - return schema, nil + if schema.Items != nil { + if len(schema.Items.Ref) > 0 && schema.Items.Ref[0] == '#' { + schema.Items.Ref = remoteComponent + schema.Items.Ref + } else if schema.Items.Value != nil { + propagateRemoteRefs(remoteComponent, schema.Items.Value) + } + } + + if schema.AdditionalProperties.Schema != nil { + ap := schema.AdditionalProperties.Schema + if len(ap.Ref) > 0 && ap.Ref[0] == '#' { + ap.Ref = remoteComponent + ap.Ref + } else if ap.Value != nil { + propagateRemoteRefs(remoteComponent, ap.Value) + } + } + + for _, list := range [][]*openapi3.SchemaRef{schema.AllOf, schema.AnyOf, schema.OneOf} { + for _, ref := range list { + if len(ref.Ref) > 0 && ref.Ref[0] == '#' { + ref.Ref = remoteComponent + ref.Ref + } else if ref.Value != nil { + propagateRemoteRefs(remoteComponent, ref.Value) + } + } + } + + if schema.Not != nil { + if len(schema.Not.Ref) > 0 && schema.Not.Ref[0] == '#' { + schema.Not.Ref = remoteComponent + schema.Not.Ref + } else if schema.Not.Value != nil { + propagateRemoteRefs(remoteComponent, schema.Not.Value) + } + } } -func mergeAllOf(allOf []*openapi3.SchemaRef) (openapi3.Schema, error) { +func mergeAllOf(allOf []*openapi3.SchemaRef, seenSchemaRef map[string]bool) (openapi3.Schema, error) { var schema openapi3.Schema for _, schemaRef := range allOf { var err error - schema, err = mergeOpenapiSchemas(schema, *schemaRef.Value, true) + if schemaRef.Ref != "" && seenSchemaRef[schemaRef.Ref] { + continue + } + if schemaRef.Ref != "" { + seenSchemaRef[schemaRef.Ref] = true + } + // Use valueWithPropagatedRef so sibling extensions on a $ref + // member of a transitively-flattened allOf reach the merged + // schema, matching mergeSchemas' top-level handling. + member, err := valueWithPropagatedRef(schemaRef) + if err != nil { + return openapi3.Schema{}, err + } + schema, err = mergeOpenapiSchemas(schema, member, true, seenSchemaRef) if err != nil { return openapi3.Schema{}, fmt.Errorf("error merging schemas for AllOf: %w", err) } @@ -84,40 +250,46 @@ func mergeAllOf(allOf []*openapi3.SchemaRef) (openapi3.Schema, error) { // mergeOpenapiSchemas merges two openAPI schemas and returns the schema // all of whose fields are composed. -func mergeOpenapiSchemas(s1, s2 openapi3.Schema, allOf bool) (openapi3.Schema, error) { +func mergeOpenapiSchemas(s1, s2 openapi3.Schema, allOf bool, seenSchemaRef map[string]bool) (openapi3.Schema, error) { var result openapi3.Schema - result.Extensions = make(map[string]interface{}) - for k, v := range s1.Extensions { - result.Extensions[k] = v - } - for k, v := range s2.Extensions { - // TODO: Check for collisions - result.Extensions[k] = v - } + result.Extensions = make(map[string]any, len(s1.Extensions)+len(s2.Extensions)) + maps.Copy(result.Extensions, s1.Extensions) + // TODO: Check for collisions + maps.Copy(result.Extensions, s2.Extensions) - result.OneOf = append(s1.OneOf, s2.OneOf...) + // Capture top-level OneOf/AnyOf before overwriting s1/s2 with transitive + // AllOf merges. The merges may surface additional OneOf/AnyOf members from + // nested allOf members (issue #1905), so we accumulate from both sources. + oneOf := append(s1.OneOf, s2.OneOf...) + anyOf := append(s1.AnyOf, s2.AnyOf...) // We are going to make AllOf transitive, so that merging an AllOf that // contains AllOf's will result in a flat object. var err error if s1.AllOf != nil { var merged openapi3.Schema - merged, err = mergeAllOf(s1.AllOf) + merged, err = mergeAllOf(s1.AllOf, seenSchemaRef) if err != nil { return openapi3.Schema{}, fmt.Errorf("error transitive merging AllOf on schema 1") } + oneOf = append(oneOf, merged.OneOf...) + anyOf = append(anyOf, merged.AnyOf...) s1 = merged } if s2.AllOf != nil { var merged openapi3.Schema - merged, err = mergeAllOf(s2.AllOf) + merged, err = mergeAllOf(s2.AllOf, seenSchemaRef) if err != nil { return openapi3.Schema{}, fmt.Errorf("error transitive merging AllOf on schema 2") } + oneOf = append(oneOf, merged.OneOf...) + anyOf = append(anyOf, merged.AnyOf...) s2 = merged } + result.OneOf = oneOf + result.AnyOf = anyOf result.AllOf = append(s1.AllOf, s2.AllOf...) if s1.Type.Slice() != nil && s2.Type.Slice() != nil && !equalTypes(s1.Type, s2.Type) { @@ -155,23 +327,53 @@ func mergeOpenapiSchemas(s1, s2 openapi3.Schema, allOf bool) (openapi3.Schema, e } result.UniqueItems = s1.UniqueItems - if s1.ExclusiveMin != s2.ExclusiveMin { + if !reflect.DeepEqual(s1.ExclusiveMin, s2.ExclusiveMin) { return openapi3.Schema{}, errors.New("merging two schemas with different ExclusiveMin") } result.ExclusiveMin = s1.ExclusiveMin - if s1.ExclusiveMax != s2.ExclusiveMax { + if !reflect.DeepEqual(s1.ExclusiveMax, s2.ExclusiveMax) { return openapi3.Schema{}, errors.New("merging two schemas with different ExclusiveMax") } result.ExclusiveMax = s1.ExclusiveMax - if s1.Nullable != s2.Nullable { - return openapi3.Schema{}, errors.New("merging two schemas with different Nullable") - + // Compare nullability via schemaIsNullable so this works the same way + // regardless of spec version: in 3.0 it reads s.Nullable, in 3.1 it + // reads "null" from the type array. Type merging itself is NOT version + // branched -- the equalTypes() check at result.Type assignment above + // uses the same slice-comparison code path in both modes: + // + // 3.0: ["string"] vs ["string"] -> equal -> merged + // 3.1: ["string","null"] vs ["string","null"] -> equal -> merged + // 3.1 mismatch: ["string","null"] vs ["string"] -> length differs + // -> error from + // equalTypes + // + // Because result.Type was already assigned (line 232) and carries any + // "null" entry forward, the merged result is correctly nullable in 3.1 + // without needing to touch result.Nullable. The result.Nullable copy + // below is a no-op in 3.1 (s1.Nullable is always false there) but kept + // for 3.0 correctness, where Nullable is the only nullability carrier. + // + // Nullability is UNIONed rather than required to match: if any member + // is nullable, the merged schema is nullable. This supports the common + // OpenAPI 3.0 idiom of decorating a $ref with nullability, which is only + // expressible through allOf because 3.0 forbids siblings next to $ref + // (issue #1898): + // + // allOf: + // - $ref: "#/components/schemas/user" + // - nullable: true + // + // kin-openapi represents Nullable as a plain bool, so an unset value is + // indistinguishable from an explicit `nullable: false`; erroring on a + // mismatch made this widely-used idiom unusable. Union is also + // consistent with how Required is merged below. + if schemaIsNullable(&s1) || schemaIsNullable(&s2) { + result.Nullable = true } - result.Nullable = s1.Nullable if s1.ReadOnly != s2.ReadOnly { return openapi3.Schema{}, errors.New("merging two schemas with different ReadOnly") @@ -195,14 +397,10 @@ func mergeOpenapiSchemas(s1, s2 openapi3.Schema, allOf bool) (openapi3.Schema, e result.Required = append(s1.Required, s2.Required...) // We merge all properties - result.Properties = make(map[string]*openapi3.SchemaRef) - for k, v := range s1.Properties { - result.Properties[k] = v - } - for k, v := range s2.Properties { - // TODO: detect conflicts - result.Properties[k] = v - } + result.Properties = make(map[string]*openapi3.SchemaRef, len(s1.Properties)+len(s2.Properties)) + maps.Copy(result.Properties, s1.Properties) + // TODO: detect conflicts + maps.Copy(result.Properties, s2.Properties) if isAdditionalPropertiesExplicitFalse(&s1) || isAdditionalPropertiesExplicitFalse(&s2) { result.WithoutAdditionalProperties() @@ -227,6 +425,17 @@ func mergeOpenapiSchemas(s1, s2 openapi3.Schema, allOf bool) (openapi3.Schema, e return openapi3.Schema{}, errors.New("merging two schemas with discriminators is not supported") } + // For allOf merges, propagate a discriminator if only one schema has it. + // Merging two different discriminators is not supported. + if s1.Discriminator != nil && s2.Discriminator != nil { + return openapi3.Schema{}, errors.New("merging two schemas with discriminators is not supported") + } + if s1.Discriminator != nil { + result.Discriminator = s1.Discriminator + } else if s2.Discriminator != nil { + result.Discriminator = s2.Discriminator + } + return result, nil } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/merge_schemas_v1.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/merge_schemas_v1.go index af8358238..b82eb3e62 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/merge_schemas_v1.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/merge_schemas_v1.go @@ -3,6 +3,7 @@ package codegen import ( "errors" "fmt" + "slices" "strings" "github.com/getkin/kin-openapi/openapi3" @@ -100,7 +101,7 @@ func GenStructFromAllOf(allOf []*openapi3.SchemaRef, path []string) (string, err } additionalPropertiesPart := fmt.Sprintf("AdditionalProperties map[string]%s `json:\"-\"`", addPropsType) - if !StringInArray(additionalPropertiesPart, objectParts) { + if !slices.Contains(objectParts, additionalPropertiesPart) { objectParts = append(objectParts, additionalPropertiesPart) } } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/operations.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/operations.go index e4d978470..6e0447a5d 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/operations.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/operations.go @@ -16,8 +16,11 @@ package codegen import ( "bufio" "bytes" + "cmp" "fmt" - "sort" + "hash/fnv" + "maps" + "slices" "strconv" "strings" "text/template" @@ -34,6 +37,12 @@ type ParameterDefinition struct { Required bool // Is this a required parameter? Spec *openapi3.Parameter Schema Schema + + // Shared is true for a parameter declared at the path-item level, which + // is inherited by every method on the path. Its helper types are declared + // once for the path item rather than once per operation, so operation type + // collection skips them (issue #2090). + Shared bool } // TypeDef is here as an adapter after a large refactoring so that I don't @@ -44,15 +53,70 @@ func (pd ParameterDefinition) TypeDef() string { return typeDecl } +// RequiresNilCheck indicates whether the generated property should have a nil check performed on it before other checks. +// This should be used in templates when performing `nil` checks, but NOT when i.e. determining if there should be an optional pointer given to the type - in that case, use `HasOptionalPointer` +func (pd ParameterDefinition) RequiresNilCheck() bool { + return pd.ZeroValueIsNil() || pd.HasOptionalPointer() +} + +// ZeroValueIsNil is a helper function to determine if the given Go type used +// for this property has `nil` as its Go zero value. Slices (OpenAPI `array`) +// and maps (OpenAPI `object` with only `additionalProperties`, rendered as +// `map[K]V`) both satisfy this — templates use it to decide whether to emit a +// nil-check before reading the field. +func (pd ParameterDefinition) ZeroValueIsNil() bool { + if pd.Schema.OAPISchema == nil { + return false + } + + if schemaPrimaryType(pd.Schema.OAPISchema.Type).Is("array") { + return true + } + + return strings.HasPrefix(pd.Schema.GoType, "map[") +} + // JsonTag generates the JSON annotation to map GoType to json type name. If Parameter // Foo is marshaled to json as "foo", this will create the annotation // 'json:"foo"' +// It also includes any additional struct tags from x-oapi-codegen-extra-tags +// at the parameter or schema level (parameter-level takes precedence). func (pd *ParameterDefinition) JsonTag() string { + fieldTags := make(map[string]string) + if pd.Required { - return fmt.Sprintf("`json:\"%s\"`", pd.ParamName) + fieldTags["json"] = pd.ParamName } else { - return fmt.Sprintf("`json:\"%s,omitempty\"`", pd.ParamName) + fieldTags["json"] = pd.ParamName + ",omitempty" + } + + // Merge x-oapi-codegen-extra-tags from schema level first, then parameter level + // so that parameter-level takes precedence. + if pd.Spec != nil && pd.Spec.Schema != nil && pd.Spec.Schema.Value != nil { + if extension, ok := pd.Spec.Schema.Value.Extensions[extPropExtraTags]; ok { + if tags, err := extExtraTags(extension); err == nil { + for k, v := range tags { + fieldTags[k] = v + } + } + } + } + if pd.Spec != nil { + if extension, ok := pd.Spec.Extensions[extPropExtraTags]; ok { + if tags, err := extExtraTags(extension); err == nil { + for k, v := range tags { + fieldTags[k] = v + } + } + } + } + + keys := SortedMapKeys(fieldTags) + tags := make([]string, len(keys)) + for i, k := range keys { + tags[i] = fmt.Sprintf(`%s:"%s"`, k, fieldTags[k]) } + return "`" + strings.Join(tags, " ") + "`" } func (pd *ParameterDefinition) IsJson() bool { @@ -114,6 +178,34 @@ func (pd *ParameterDefinition) Explode() bool { return *pd.Spec.Explode } +// SchemaType returns the first OpenAPI type string for this parameter's schema (e.g. "string", "integer"), +// or empty string if unavailable. +func (pd *ParameterDefinition) SchemaType() string { + if pd.Spec.Schema != nil && pd.Spec.Schema.Value != nil && pd.Spec.Schema.Value.Type != nil { + if s := pd.Spec.Schema.Value.Type.Slice(); len(s) > 0 { + return s[0] + } + } + return "" +} + +// SchemaFormat returns the OpenAPI format string for this parameter's schema (e.g. "byte", "date-time"), +// or empty string if unavailable. +func (pd *ParameterDefinition) SchemaFormat() string { + if pd.Spec.Schema != nil && pd.Spec.Schema.Value != nil { + return pd.Spec.Schema.Value.Format + } + return "" +} + +// SanitizedParamName returns the parameter name sanitized to be a valid Go +// identifier. This is needed for routers like net/http's ServeMux where path +// wildcards (e.g. {name}) must be valid Go identifiers. For the original +// OpenAPI parameter name (e.g. for error messages or JSON tags), use ParamName. +func (pd ParameterDefinition) SanitizedParamName() string { + return SanitizeGoIdentifier(pd.ParamName) +} + func (pd ParameterDefinition) GoVariableName() string { name := LowercaseFirstCharacters(pd.GoName()) if IsGoKeyword(name) { @@ -143,7 +235,7 @@ func (pd ParameterDefinition) IndirectOptional() bool { // HasOptionalPointer indicates whether the generated property has an optional pointer associated with it. // This takes into account the `x-go-type-skip-optional-pointer` extension, allowing a parameter definition to control whether the pointer should be skipped. func (pd ParameterDefinition) HasOptionalPointer() bool { - return pd.Required == false && pd.Schema.SkipOptionalPointer == false //nolint:staticcheck + return !pd.Required && !pd.Schema.SkipOptionalPointer } type ParameterDefinitions []ParameterDefinition @@ -161,7 +253,7 @@ func (p ParameterDefinitions) FindByName(name string) *ParameterDefinition { // descriptors into a flat list. This makes it a lot easier to traverse the // data in the template engine. func DescribeParameters(params openapi3.Parameters, path []string) ([]ParameterDefinition, error) { - outParams := make([]ParameterDefinition, 0) + outParams := make([]ParameterDefinition, 0, len(params)) for _, paramOrRef := range params { param := paramOrRef.Value @@ -179,6 +271,17 @@ func DescribeParameters(params openapi3.Parameters, path []string) ([]ParameterD Schema: goType, } + // A parameter-level `x-go-type-skip-optional-pointer` overrides the + // schema-level setting. `GenStructFromSchema` applies the same override + // when rendering the params struct; without mirroring it here, the + // client/server templates disagree with the struct definition and emit + // a dereference (`*params.Field`) on a field declared without a pointer. + if extension, ok := param.Extensions[extPropGoTypeSkipOptionalPointer]; ok { + if skipOptionalPointer, err := extParsePropGoTypeSkipOptionalPointer(extension); err == nil { + pd.Schema.SkipOptionalPointer = skipOptionalPointer + } + } + // If this is a reference to a predefined type, simply use the reference // name as the type. $ref: "#/components/schemas/custom_type" becomes // "CustomType". @@ -195,6 +298,253 @@ func DescribeParameters(params openapi3.Parameters, path []string) ([]ParameterD return outParams, nil } +// paramNeedsHoisting reports whether a parameter's schema produces a named +// helper type — an anyOf/oneOf union member, an inline object, etc. — as +// opposed to a bare primitive. Only such parameters can cause the redeclaration +// collision fixed for issue #2090, so only they participate in collision +// detection. The check is exact: it describes the parameter and asks whether it +// hoisted anything, rather than second-guessing which schema shapes hoist. +func paramNeedsHoisting(paramRef *openapi3.ParameterRef) (bool, error) { + if paramRef == nil || paramRef.Value == nil { + return false, nil + } + described, err := DescribeParameters(openapi3.Parameters{paramRef}, nil) + if err != nil { + return false, err + } + for _, pd := range described { + if len(pd.Schema.AdditionalTypes) > 0 { + return true, nil + } + } + return false, nil +} + +// sharedParamScope is one path item whose path-item-level parameters are shared +// by every method on it. hashKey is a string unique to the scope, hashed to +// disambiguate colliding names; source is a human-readable identifier for the +// scope used in generated doc comments. +type sharedParamScope struct { + item *openapi3.PathItem + hashKey string + source string +} + +// enumerateSharedParamScopes returns every path item in the spec that can +// declare path-item-level (shared) parameters: regular paths, webhooks, and +// callback path items. All three share the one global Go type namespace, so +// they must be considered together when detecting and resolving collisions. +func enumerateSharedParamScopes(swagger *openapi3.T) []sharedParamScope { + var scopes []sharedParamScope + + if swagger.Paths != nil { + for _, requestPath := range SortedMapKeys(swagger.Paths.Map()) { + item := swagger.Paths.Value(requestPath) + if item != nil { + scopes = append(scopes, sharedParamScope{item: item, hashKey: requestPath, source: requestPath}) + } + } + } + for _, webhookName := range SortedMapKeys(swagger.Webhooks) { + if item := swagger.Webhooks[webhookName]; item != nil { + scopes = append(scopes, sharedParamScope{item: item, hashKey: "webhook:" + webhookName, source: "webhook " + webhookName}) + } + } + if swagger.Paths != nil { + for _, requestPath := range SortedMapKeys(swagger.Paths.Map()) { + pathItem := swagger.Paths.Value(requestPath) + if pathItem == nil { + continue + } + for _, parentMethod := range SortedMapKeys(pathItem.Operations()) { + parentOp := pathItem.Operations()[parentMethod] + for _, cbName := range SortedMapKeys(parentOp.Callbacks) { + cbRef := parentOp.Callbacks[cbName] + if cbRef == nil || cbRef.Value == nil { + continue + } + cb := cbRef.Value + cbKeys := append([]string(nil), cb.Keys()...) + slices.Sort(cbKeys) + for _, urlExpr := range cbKeys { + if item := cb.Value(urlExpr); item != nil { + scopes = append(scopes, sharedParamScope{ + item: item, + hashKey: "callback:" + requestPath + ":" + parentMethod + ":" + cbName + ":" + urlExpr, + source: "callback " + cbName + " " + urlExpr, + }) + } + } + } + } + } + } + return scopes +} + +// resolveSharedParameters is the pre-pass for shared (path-item-level) +// parameter naming (issue #2090). It describes each scope's shared parameters +// once and returns them keyed by path item, ready for the operation loops to +// attach. +// +// A shared parameter's helper types are declared once for its path item, so +// two methods on the same path never collide. Names still collide *across* +// path items, though — the same `{id}` reused by sibling paths is the common +// case — so a helper-type name produced by more than one scope is disambiguated +// by prefixing every colliding scope's parameters with a short, stable hash of +// the scope (git-style: extended to the full hash only if two scopes' short +// hashes clash). A parameter that doesn't collide keeps its historical +// undecorated name, so existing generated code is unaffected. +func resolveSharedParameters(swagger *openapi3.T) (map[*openapi3.PathItem][]ParameterDefinition, error) { + scopes := enumerateSharedParamScopes(swagger) + + // Count, per shared-parameter name, how many scopes hoist a helper type + // under it. A name produced by two or more scopes collides. + nameCounts := map[string]int{} + for _, scope := range scopes { + if len(scope.item.Operations()) == 0 { + continue + } + for _, paramRef := range scope.item.Parameters { + hoists, err := paramNeedsHoisting(paramRef) + if err != nil { + return nil, err + } + if hoists { + nameCounts[paramRef.Value.Name]++ + } + } + } + colliding := map[string]bool{} + for name, n := range nameCounts { + if n >= 2 { + colliding[name] = true + } + } + + // Assign a disambiguating token to every scope that carries a colliding + // parameter. Tokens are hashes of the scope key; a short prefix is used + // unless two scopes' short prefixes clash, in which case both fall back to + // the full hash. + tokenScopeKeys := map[*openapi3.PathItem]string{} + var needToken []string + for _, scope := range scopes { + if len(scope.item.Operations()) == 0 { + continue + } + for _, paramRef := range scope.item.Parameters { + if paramRef.Value != nil && colliding[paramRef.Value.Name] { + tokenScopeKeys[scope.item] = scope.hashKey + needToken = append(needToken, scope.hashKey) + break + } + } + } + tokens := assignScopeTokens(needToken) + + result := map[*openapi3.PathItem][]ParameterDefinition{} + for _, scope := range scopes { + if _, done := result[scope.item]; done { + continue + } + token := "" + if key, ok := tokenScopeKeys[scope.item]; ok { + token = tokens[key] + } + described, err := describeSharedParameters(scope.item.Parameters, scope.source, token, colliding) + if err != nil { + return nil, err + } + markShared(described) + result[scope.item] = described + } + return result, nil +} + +// assignScopeTokens maps each scope key to a stable identifier token derived +// from an FNV hash of the key. It uses a short prefix of the hash, extending +// any keys whose short prefixes collide to the full hash (git-style). The +// tokens are lowercase so they camel-case cleanly when threaded through type +// naming (e.g. "a1b2c3d" becomes the "A1b2c3d" prefix of "A1b2c3dId0"). +func assignScopeTokens(keys []string) map[string]string { + const shortLen = 7 + full := map[string]string{} + short := map[string]string{} + for _, key := range keys { + h := fnv.New64a() + _, _ = h.Write([]byte(key)) + sum := fmt.Sprintf("h%x", h.Sum64()) + full[key] = sum + short[key] = sum[:min(shortLen+1, len(sum))] + } + shortCounts := map[string]int{} + for _, key := range keys { + shortCounts[short[key]]++ + } + tokens := map[string]string{} + for _, key := range keys { + if shortCounts[short[key]] > 1 { + tokens[key] = full[key] + } else { + tokens[key] = short[key] + } + } + return tokens +} + +// describeSharedParameters describes a scope's shared parameters once. A +// parameter whose name collides across scopes is prefixed with the scope's +// disambiguating token so its helper types are uniquely named; a parameter +// that does not collide keeps its historical undecorated name (issue #2090). +// For the disambiguated types, a doc comment is set explaining the otherwise +// opaque hash prefix and pointing back to the source path. +func describeSharedParameters(params openapi3.Parameters, source, token string, colliding map[string]bool) ([]ParameterDefinition, error) { + out := make([]ParameterDefinition, 0, len(params)) + for _, paramRef := range params { + collides := token != "" && paramRef.Value != nil && colliding[paramRef.Value.Name] + var path []string + if collides { + path = []string{token} + } + described, err := DescribeParameters(openapi3.Parameters{paramRef}, path) + if err != nil { + return nil, err + } + if collides { + for i := range described { + for j := range described[i].Schema.AdditionalTypes { + td := &described[i].Schema.AdditionalTypes[j] + td.Comment = fmt.Sprintf( + "// %s is a helper type for the shared %q parameter of %q, prefixed with a per-path hash to disambiguate it from the same-named parameter on another path.", + td.TypeName, paramRef.Value.Name, source) + } + } + } + out = append(out, described...) + } + return out, nil +} + +// markShared flags every parameter as shared at the path-item level, so its +// helper types are declared once for the path item rather than once per +// operation (issue #2090). +func markShared(params []ParameterDefinition) { + for i := range params { + params[i].Shared = true + } +} + +// sharedParameterTypeDefs returns the helper TypeDefinitions produced by +// path-item-level parameters. These are emitted once for the path item +// (attributed to its first operation) instead of once per operation. +func sharedParameterTypeDefs(sharedParams []ParameterDefinition) []TypeDefinition { + var typeDefs []TypeDefinition + for _, param := range sharedParams { + typeDefs = append(typeDefs, param.Schema.AdditionalTypes...) + } + return typeDefs +} + type SecurityDefinition struct { ProviderName string Scopes []string @@ -204,6 +554,9 @@ func DescribeSecurityDefinition(securityRequirements openapi3.SecurityRequiremen outDefs := make([]SecurityDefinition, 0) for _, sr := range securityRequirements { + if len(sr) == 0 { + return nil + } for _, k := range SortedMapKeys(sr) { v := sr[k] outDefs = append(outDefs, SecurityDefinition{ProviderName: k, Scopes: v}) @@ -213,10 +566,27 @@ func DescribeSecurityDefinition(securityRequirements openapi3.SecurityRequiremen return outDefs } +// filterOutUndefinedSecuritySchemes drops any SecurityDefinition whose ProviderName +// is not present in defined. A `security` requirement that references an +// unknown scheme would otherwise produce a constant declaration and middleware +// references against a context-key type that is never emitted (the type is +// only generated for entries in components/securitySchemes). +func filterOutUndefinedSecuritySchemes(defs []SecurityDefinition, defined map[string]struct{}) []SecurityDefinition { + out := make([]SecurityDefinition, 0, len(defs)) + for _, d := range defs { + if _, ok := defined[d.ProviderName]; ok { + out = append(out, d) + } + } + return out +} + // OperationDefinition describes an Operation type OperationDefinition struct { // OperationId is the `operationId` field from the OpenAPI Specification, after going through a `nameNormalizer`, and will be used to generate function names OperationId string + // SpecOperationId is the raw `operationId` value as it appears in the OpenAPI spec, before normalization to a Go identifier. Empty when the spec didn't supply one (in which case the codegen-generated ID is the only available identifier and is exposed via OperationId). + SpecOperationId string PathParams []ParameterDefinition // Parameters in the path, eg, /path/:param HeaderParams []ParameterDefinition // Parameters in HTTP headers @@ -230,7 +600,89 @@ type OperationDefinition struct { Summary string // Summary string from Swagger, used to generate a comment Method string // GET, POST, DELETE, etc. Path string // The Swagger path for the operation, like /resource/{id} - Spec *openapi3.Operation + // SpecOrder is the source line on which this operation's path is + // declared in the spec, used to register routes in the order the paths + // appear in the spec rather than sorted (issue #1887). Zero when the + // source location is unavailable (e.g. a programmatically-built spec), + // in which case route registration falls back to the default order. + SpecOrder int + Spec *openapi3.Operation + IsAlias bool // True when this path is a $ref alias of another path item + AliasTarget string // When IsAlias is true, this is the OperationId of the canonical operation (for route registration to reference the correct wrapper) + PathItemRef string // The path item's $ref (if any); used to qualify externally-loaded schemas referenced from this operation's responses + + // IsWebhook is true when this OperationDefinition was sourced from + // spec.Webhooks (OpenAPI 3.1+). Webhook operations have no path + // template; the target URL is supplied per-call by the initiator. + IsWebhook bool + // WebhookName is the spec.Webhooks map key when IsWebhook is true. + WebhookName string + + // IsCallback is true when this OperationDefinition was sourced from + // a parent operation's `callbacks:` block (OpenAPI 3.0+). Callback + // operations have no path template at codegen time; the target URL + // is the runtime callback URL discovered via the spec's callback + // expression (typically a field on the parent operation's request + // body) and is supplied per-call by the initiator. + IsCallback bool + // CallbackName is the parent operation's `callbacks:` map key + // (e.g. "treePlanted") when IsCallback is true. + CallbackName string +} + +// HandlerName returns the OperationId to use when referencing the server-side +// wrapper function. For alias operations this is the canonical operation's ID, +// since the alias doesn't generate its own wrapper. +func (o *OperationDefinition) HandlerName() string { + if o.IsAlias { + return o.AliasTarget + } + return o.OperationId +} + +// MiddlewareKey returns the identifier to use as the key in per-operation +// middleware maps. The raw spec OperationId is preferred so map keys mirror +// the OpenAPI spec verbatim; falls back to the normalized OperationId when +// the spec didn't supply one. +func (o *OperationDefinition) MiddlewareKey() string { + if o.SpecOperationId != "" { + return o.SpecOperationId + } + return o.OperationId +} + +// SourceName returns WebhookName when IsWebhook, CallbackName when +// IsCallback, or empty otherwise. Templates use this to label the +// emitted handler uniformly without branching on which kind of source +// the operation came from. +func (o OperationDefinition) SourceName() string { + if o.IsWebhook { + return o.WebhookName + } + if o.IsCallback { + return o.CallbackName + } + return "" +} + +// ReceiverTemplateData is the input to the per-framework webhook / +// callback receiver template. Prefix selects between "Webhook" and +// "Callback" (and the lowercase form for prose), so a single template +// per framework handles both kinds. +type ReceiverTemplateData struct { + Prefix string // "Webhook" or "Callback" + PrefixLower string // lowercase form of Prefix, for prose + Operations []OperationDefinition +} + +// NewReceiverTemplateData builds the template input for the given +// prefix ("Webhook" or "Callback") and operation list. +func NewReceiverTemplateData(prefix string, ops []OperationDefinition) ReceiverTemplateData { + return ReceiverTemplateData{ + Prefix: prefix, + PrefixLower: strings.ToLower(prefix), + Operations: ops, + } } // Params returns the list of all parameters except Path parameters. Path parameters @@ -263,19 +715,146 @@ func (o *OperationDefinition) HasBody() bool { return o.Spec.RequestBody != nil } -// SummaryAsComment returns the Operations summary as a multi line comment -func (o *OperationDefinition) SummaryAsComment() string { +// SummaryAsComment returns the Operations summary as a Godoc-style multi line comment +func (o *OperationDefinition) SummaryAsComment(prefix string) string { if o.Summary == "" { return "" } - trimmed := strings.TrimSuffix(o.Summary, "\n") - parts := strings.Split(trimmed, "\n") + parts := strings.Split(normalizeWhitespace(o.Summary), "\n") for i, p := range parts { - parts[i] = "// " + p + if i == 0 && prefix != "" { + parts[i] = "// " + prefix + " " + p + } else { + parts[i] = "// " + p + } } return strings.Join(parts, "\n") } +// prepareDescriptionLines normalises description for inclusion in a Godoc comment, and: +// +// 1. Returns nil when the description is the same as the summary +// 1. Ensures a single-line description includes a trailing `.`, which prevents +// gofmt from promoting this to an "old-style" header +func prepareDescriptionLines(summary, description string) []string { + description = normalizeWhitespace(description) + if description == "" { + return nil + } + if description == normalizeWhitespace(summary) { + return nil + } + lines := strings.Split(description, "\n") + if len(lines) == 1 && !strings.ContainsAny(lines[0], ".,;:!?") { + lines[0] += "." + } + return lines +} + +// GenerateFunctionComment returns a full Godoc-style multi-line comment with: +// - the Summary, if present, as the first line of the comment +// - if not present, an indication of the HTTP call this corresponds with +// - the Description, if present +// - whether this function takes a body and a content type +// +// Takes originalFunctionName (the OperationId or the function name being generated for this Operation), a suffix (if necessary) and whether this is being generated for ClientInterface or ClientWithResponsesInterface +func (o OperationDefinition) GenerateFunctionComment(originalFunctionName string, functionSuffix string, isFunctionWithResponses bool) string { + functionName := originalFunctionName + functionSuffix + descriptionLines := prepareDescriptionLines(o.Summary, o.Spec.Description) + + var parts []string + if summary := o.SummaryAsComment(functionName); summary != "" { + parts = append(parts, strings.Split(summary, "\n")...) + parts = append(parts, "//") + if len(descriptionLines) > 0 { + for _, line := range descriptionLines { + parts = append(parts, "// "+line) + } + parts = append(parts, "//") + } + if o.HasBody() { + if isFunctionWithResponses { + parts = append(parts, "// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s).") + parts = append(parts, "//") + } else { + parts = append(parts, "// Takes any type of body and a specified content type.") + parts = append(parts, "//") + } + } else { + if isFunctionWithResponses { + parts = append(parts, "// Returns a wrapper object for the known response body format(s).") + parts = append(parts, "//") + } + } + parts = append(parts, "// Corresponds with "+o.Method+" "+o.Path+" (the `"+o.OperationId+"` operationId).") + } else { + if o.HasBody() { + parts = append(parts, "// "+functionName+" performs a "+o.Method+" "+o.Path+" (the `"+o.OperationId+"` operationId) request,") + parts = append(parts, "// with any type of body and a specified content type.") + } else { + parts = append(parts, "// "+functionName+" performs a "+o.Method+" "+o.Path+" (the `"+o.OperationId+"` operationId) request.") + } + if len(descriptionLines) > 0 { + parts = append(parts, "//") + for _, line := range descriptionLines { + parts = append(parts, "// "+line) + } + } + if isFunctionWithResponses { + parts = append(parts, "//") + parts = append(parts, "// Returns a wrapper object for the known response body format(s).") + } + } + + // make sure that each line is sanitised + for i, part := range parts { + parts[i] = stripNewLines(part) + } + + return strings.Join(parts, "\n") +} + +// DeprecationComment returns a Go-style deprecation comment if the operation is deprecated, otherwise returns an empty string. +func (o *OperationDefinition) DeprecationComment() string { + if o.Spec == nil || !o.Spec.Deprecated { + return "" + } + reason := "this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set" + if extension, ok := o.Spec.Extensions[extDeprecationReason]; ok { + if r, err := extParseDeprecationReason(extension); err == nil { + reason = r + } + } + return DeprecationComment(reason) +} + +// responseMediaTypeSuffix returns the media-type discriminator suffix (e.g. +// "ApplicationJSON") that must be appended to the Go type name generated for a +// single content entry of a response, or "" when the base name is used as-is. +// +// A suffix is required only when one response carries more than one +// JSON-compatible content type, and only the JSON entries receive one: non-JSON +// media types (XML, YAML, ...) never get a dedicated generated type, so they +// reuse the base name. Both the type declaration (GenerateTypesForResponses) and +// the client response-wrapper field types (GetResponseTypeDefinitions) must make +// this identical decision — otherwise the wrapper references per-content-type +// type names that were never declared (see issue #2389). +func responseMediaTypeSuffix(content openapi3.Content, mediaType string) string { + if !util.IsMediaTypeJson(mediaType) { + return "" + } + jsonCount := 0 + for mt := range content { + if util.IsMediaTypeJson(mt) { + jsonCount++ + } + } + if jsonCount <= 1 { + return "" + } + return mediaTypeToCamelCase(mediaType) +} + // GetResponseTypeDefinitions produces a list of type definitions for a given Operation for the response // types which we know how to parse. These will be turned into fields on a // response object for automatic deserialization of responses in the generated @@ -293,47 +872,70 @@ func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefini // We can only generate a type if we have a value: if responseRef.Value != nil { - jsonCount := 0 - for mediaType := range responseRef.Value.Content { - if util.IsMediaTypeJson(mediaType) { - jsonCount++ - } - } - sortedContentKeys := SortedMapKeys(responseRef.Value.Content) for _, contentTypeName := range sortedContentKeys { contentType := responseRef.Value.Content[contentTypeName] // We can only generate a type if we have a schema: if contentType.Schema != nil { - responseSchema, err := GenerateGoSchema(contentType.Schema, []string{o.OperationId, responseName}) - if err != nil { - return nil, fmt.Errorf("unable to determine Go type for %s.%s: %w", o.OperationId, contentTypeName, err) - } - - var typeName string + var typeName, tag string switch { // HAL+JSON: - case StringInArray(contentTypeName, contentTypesHalJSON): + case slices.Contains(contentTypesHalJSON, contentTypeName): typeName = fmt.Sprintf("HALJSON%s", nameNormalizer(responseName)) + tag = "HALJSON" case contentTypeName == "application/json": // if it's the standard application/json typeName = fmt.Sprintf("JSON%s", nameNormalizer(responseName)) + tag = "JSON" // Vendored JSON - case StringInArray(contentTypeName, contentTypesJSON) || util.IsMediaTypeJson(contentTypeName): + case slices.Contains(contentTypesJSON, contentTypeName) || util.IsMediaTypeJson(contentTypeName): baseTypeName := fmt.Sprintf("%s%s", nameNormalizer(contentTypeName), nameNormalizer(responseName)) typeName = strings.ReplaceAll(baseTypeName, "Json", "JSON") + tag = mediaTypeToCamelCase(contentTypeName) // YAML: - case StringInArray(contentTypeName, contentTypesYAML): + case slices.Contains(contentTypesYAML, contentTypeName): typeName = fmt.Sprintf("YAML%s", nameNormalizer(responseName)) + tag = "YAML" // XML: - case StringInArray(contentTypeName, contentTypesXML): + case slices.Contains(contentTypesXML, contentTypeName): typeName = fmt.Sprintf("XML%s", nameNormalizer(responseName)) + tag = "XML" default: continue } + // Use the same body-type name as the server-side + // GenerateResponseDefinitions ("Body" suffixed so it + // doesn't collide with the strict envelope's struct + // wrapper) as the schema-path root. The canonical + // declaration happens server-side; here we just point + // RefType at the same name so the JSON field + // renders as a pointer to it. + responseBodyTypeName := o.OperationId + responseName + tag + "ResponseBody" + schemaPath := []string{responseBodyTypeName} + responseSchema, err := GenerateGoSchema(contentType.Schema, schemaPath) + if err != nil { + return nil, fmt.Errorf("unable to determine Go type for %s.%s: %w", o.OperationId, contentTypeName, err) + } + + // Hoist inline response-root schemas that need + // method-emitting boilerplate (UnionElements / + // AdditionalProperties). For external path items, + // qualify with the imported package — see the + // equivalent block in GenerateResponseDefinitions for + // rationale. + if !IsGoTypeReference(responseRef.Ref) && responseSchema.RefType == "" && + (len(responseSchema.UnionElements) != 0 || responseSchema.HasAdditionalProperties || + (globalState.options.OutputOptions.GenerateTypesForAnonymousSchemas && len(responseSchema.Properties) > 0)) { + if externalPkg := externalPackageFor(o.PathItemRef); externalPkg != "" { + responseSchema.RefType = fmt.Sprintf("%s.%s", externalPkg, responseBodyTypeName) + } else { + responseSchema.RefType = responseBodyTypeName + } + } + td := ResponseTypeDefinition{ TypeDefinition: TypeDefinition{ TypeName: typeName, @@ -343,15 +945,58 @@ func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefini ContentTypeName: contentTypeName, AdditionalTypeDefinitions: responseSchema.GetAdditionalTypeDefs(), } - if IsGoTypeReference(responseRef.Ref) { - refType, err := RefPathToGoType(responseRef.Ref) - if err != nil { - return nil, fmt.Errorf("error dereferencing response Ref: %w", err) + // A component response only declares a Go type for its JSON + // content (GenerateTypesForResponses skips non-JSON media). + // Point the wrapper field at that declared component type for + // JSON content only; non-JSON content keeps the type derived + // from its own schema above, so it neither references an + // undeclared base type (e.g. an XML-only component response) + // nor silently decodes into the JSON type when the JSON and + // non-JSON schemas differ. + if IsGoTypeReference(responseRef.Ref) && util.IsMediaTypeJson(contentTypeName) { + // Determine the imported package for an external response. + // Either the operation came from an externally-ref'd path + // item and the response ref is relative to that file + // (issue #2308), or the response ref targets an external + // file directly (issue #2422). + externalPkg := "" + if pkg := externalPackageFor(o.PathItemRef); pkg != "" && strings.HasPrefix(responseRef.Ref, "#") { + externalPkg = pkg + } else if pkg := externalPackageFor(responseRef.Ref); pkg != "" { + externalPkg = pkg } - if jsonCount > 1 && util.IsMediaTypeJson(contentTypeName) { - refType += mediaTypeToCamelCase(contentTypeName) + + if externalPkg != "" { + // The client wrapper points at the imported package's + // response *model* type. Resolve its name in the + // external document's context rather than via + // RefPathToGoType / resolvedNameForRefPath, which key + // off the root spec; in particular honour x-go-name on + // the response component the same way the external + // package generated it. + refParts := strings.Split(responseRef.Ref, "/") + refType := SchemaNameToTypeName(refParts[len(refParts)-1]) + if ext, ok := responseRef.Value.Extensions[extGoName]; ok { + if name, err := extTypeName(ext); err == nil { + refType = name + } + } + refType += responseMediaTypeSuffix(responseRef.Value.Content, contentTypeName) + td.Schema.RefType = fmt.Sprintf("%s.%s", externalPkg, refType) + } else { + refType, err := RefPathToGoType(responseRef.Ref) + if err != nil { + return nil, fmt.Errorf("error dereferencing response Ref: %w", err) + } + if suffix := responseMediaTypeSuffix(responseRef.Value.Content, contentTypeName); suffix != "" { + if resolved := resolvedNameForRefPath(responseRef.Ref, contentTypeName); resolved != "" { + refType = resolved + suffix + } else { + refType += suffix + } + } + td.Schema.RefType = refType } - td.Schema.RefType = refType } tds = append(tds, td) } @@ -361,13 +1006,10 @@ func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefini return tds, nil } -func (o OperationDefinition) HasMaskedRequestContentTypes() bool { - for _, body := range o.Bodies { - if !body.IsFixedContentType() { - return true - } - } - return false +func (o *OperationDefinition) HasMaskedRequestContentTypes() bool { + return slices.ContainsFunc(o.Bodies, func(body RequestBodyDefinition) bool { + return !body.IsFixedContentType() + }) } // RequestBodyDefinition describes a request body @@ -391,13 +1033,72 @@ type RequestBodyDefinition struct { // Contains encoding options for formdata Encoding map[string]RequestBodyEncoding + + // Deprecated indicates the parent operation is deprecated, so this body + // type alias should be marked deprecated too. + Deprecated bool + + // DeprecationReason is propagated from the parent operation's x-deprecated-reason. + DeprecationReason string +} + +// GenerateFunctionComment returns a full Godoc-style multi-line comment with: +// - the Summary, if present, as the first line of the comment +// - if not present, an indication of the HTTP call this corresponds with +// - whether this function takes a body and a content type +// +// Takes originalFunctionName (the OperationId or the function name being generated for this Operation), a suffix (if necessary) and whether this is being generated for ClientInterface or ClientWithResponsesInterface +func (r RequestBodyDefinition) GenerateFunctionComment(originalFunctionName string, parent OperationDefinition, functionSuffix string, isFunctionWithResponses bool) string { + functionName := originalFunctionName + functionSuffix + descriptionLines := prepareDescriptionLines(parent.Summary, parent.Spec.Description) + + var parts []string + if summary := parent.SummaryAsComment(functionName); summary != "" { + parts = append(parts, strings.Split(summary, "\n")...) + parts = append(parts, "//") + if len(descriptionLines) > 0 { + for _, line := range descriptionLines { + parts = append(parts, "// "+line) + } + parts = append(parts, "//") + } + if isFunctionWithResponses { + parts = append(parts, "// Takes a body of the `"+r.ContentType+"` content type, and returns a wrapper object for the known response body format(s).") + } else { + parts = append(parts, "// Takes a body of the `"+r.ContentType+"` content type.") + } + parts = append(parts, "//") + parts = append(parts, "// Corresponds with "+parent.Method+" "+parent.Path+" (the `"+parent.OperationId+"` operationId).") + } else { + parts = append(parts, "// "+functionName+" performs a "+parent.Method+" "+parent.Path+" (the `"+parent.OperationId+"` operationId) request.") + if isFunctionWithResponses { + parts = append(parts, "// Takes a body of the `"+r.ContentType+"` content type, and returns a wrapper object for the known response body format(s).") + } else { + parts = append(parts, "// Takes a body of the `"+r.ContentType+"` content type.") + } + if len(descriptionLines) > 0 { + parts = append(parts, "//") + for _, line := range descriptionLines { + parts = append(parts, "// "+line) + } + } + } + + // make sure that each line is sanitised + for i, part := range parts { + parts[i] = stripNewLines(part) + } + + return strings.Join(parts, "\n") } // TypeDef returns the Go type definition for a request body func (r RequestBodyDefinition) TypeDef(opID string) *TypeDefinition { return &TypeDefinition{ - TypeName: fmt.Sprintf("%s%sRequestBody", opID, r.NameTag), - Schema: r.Schema, + TypeName: fmt.Sprintf("%s%sRequestBody", opID, r.NameTag), + Schema: r.Schema, + Deprecated: r.Deprecated, + DeprecationReason: r.DeprecationReason, } } @@ -520,10 +1221,91 @@ func (r ResponseContentDefinition) IsJSON() bool { return util.IsMediaTypeJson(r.ContentType) } +// IsStreamingContentType reports whether this response's media type matches +// any configured streaming-content-types pattern (defaults merged with +// OutputOptions.StreamingContentTypes). Templates use this to emit a +// flush-per-chunk streaming path instead of a buffered io.Copy. +func (r ResponseContentDefinition) IsStreamingContentType() bool { + for _, re := range globalState.streamingContentTypeRegexes { + if re.MatchString(r.ContentType) { + return true + } + } + return false +} + type ResponseHeaderDefinition struct { - Name string - GoName string - Schema Schema + Name string + GoName string + Schema Schema + Required bool + Nullable bool + Deprecated bool + DeprecationReason string +} + +// DeprecationComment returns a Go-style deprecation comment if the header is deprecated, otherwise returns an empty string. +func (h ResponseHeaderDefinition) DeprecationComment() string { + if !h.Deprecated { + return "" + } + reason := h.DeprecationReason + if reason == "" { + reason = "this header has been marked as deprecated upstream, but no `x-deprecated-reason` was set" + } + return DeprecationComment(reason) +} + +// GoTypeDef returns the Go type string for this header, applying pointer or +// nullable wrapping based on the Required/Nullable fields and global config. +func (h ResponseHeaderDefinition) GoTypeDef() string { + typeDef := h.Schema.TypeDecl() + if globalState.options.OutputOptions.NullableType && h.Nullable { + return "nullable.Nullable[" + typeDef + "]" + } + if !h.Schema.SkipOptionalPointer && (!h.Required || h.Nullable) { + typeDef = "*" + typeDef + } + return typeDef +} + +// IsOptional returns true if this header's Go type is indirect (pointer or +// nullable wrapper), meaning the template should guard before calling +// w.Header().Set(). This must stay in sync with GoTypeDef(). +func (h ResponseHeaderDefinition) IsOptional() bool { + if h.IsNullable() { + return true + } + if h.Schema.SkipOptionalPointer { + return false + } + return !h.Required || h.Nullable +} + +// IsNullable returns true if the header type uses nullable.Nullable[T] +// rather than a pointer for optionality. +func (h ResponseHeaderDefinition) IsNullable() bool { + return globalState.options.OutputOptions.NullableType && h.Nullable +} + +// SchemaType returns the first OpenAPI type string for this header's schema +// (e.g. "string", "integer"), or empty string if unavailable. +func (h ResponseHeaderDefinition) SchemaType() string { + if h.Schema.OAPISchema != nil && h.Schema.OAPISchema.Type != nil { + if s := h.Schema.OAPISchema.Type.Slice(); len(s) > 0 { + return s[0] + } + } + return "" +} + +// SchemaFormat returns the OpenAPI format string for this header's schema +// (e.g. "date-time", "duration"), or empty string if unavailable. +func (h ResponseHeaderDefinition) SchemaFormat() string { + if h.Schema.OAPISchema != nil { + return h.Schema.OAPISchema.Format + } + return "" } // FilterParameterDefinitionByType returns the subset of the specified parameters which are of the @@ -539,29 +1321,57 @@ func FilterParameterDefinitionByType(params []ParameterDefinition, in string) [] } // OperationDefinitions returns all operations for a swagger definition. -func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]OperationDefinition, error) { - var operations []OperationDefinition - - var toCamelCaseFunc func(string) string - if initialismOverrides { - toCamelCaseFunc = ToCamelCaseWithInitialism - } else { - toCamelCaseFunc = ToCamelCase +// pathItemSourceLine returns the 1-based source line on which a path item's +// key is declared, when the spec was loaded with origin tracking enabled +// (see LoadSwagger). Returns 0 when the location is unavailable — e.g. a +// spec built in memory or one whose loader did not record origins. +func pathItemSourceLine(pathItem *openapi3.PathItem) int { + if pathItem == nil || pathItem.Origin == nil || pathItem.Origin.Key == nil { + return 0 } + return pathItem.Origin.Key.Line +} + +func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) { + var operations []OperationDefinition if swagger == nil || swagger.Paths == nil { return operations, nil } + // Collect the names of security schemes actually defined under + // components/securitySchemes. Requirements that reference an undefined + // scheme are filtered out below so generated code stays compilable. + definedSecuritySchemes := map[string]struct{}{} + if swagger.Components != nil { + for name := range swagger.Components.SecuritySchemes { + definedSecuritySchemes[name] = struct{}{} + } + } + + // Track alias counters for generating unique client method names + // when multiple paths $ref the same path item. + aliasCounters := map[string]int{} + + // Resolve path-item-level (shared) parameters once for the whole spec, so + // their helper types are declared once per path item and colliding names + // across paths are disambiguated (issue #2090). + sharedParams, err := resolveSharedParameters(swagger) + if err != nil { + return nil, err + } + for _, requestPath := range SortedMapKeys(swagger.Paths.Map()) { pathItem := swagger.Paths.Value(requestPath) - // These are parameters defined for all methods on a given path. They - // are shared by all methods. - globalParams, err := DescribeParameters(pathItem.Parameters, nil) - if err != nil { - return nil, fmt.Errorf("error describing global parameters for %s: %s", - requestPath, err) - } + // Source line of this path's key, so route registration can follow + // spec declaration order (issue #1887). Zero when unavailable. + pathSpecOrder := pathItemSourceLine(pathItem) + // Parameters defined for all methods on this path, resolved by the + // pre-pass. Their helper types are emitted once for the path item (on + // its first operation) rather than once per operation (issue #2090). + globalParams := sharedParams[pathItem] + sharedParamTypeDefs := sharedParameterTypeDefs(globalParams) + sharedTypeDefsEmitted := false // Each path can have a number of operations, POST, GET, OPTIONS, etc. pathOps := pathItem.Operations() @@ -574,9 +1384,14 @@ func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]Oper } // take a copy of operationId, so we don't modify the underlying spec operationId := op.OperationID + // Preserve the raw spec value (pre-normalization, pre-prefix, pre-alias-suffix) + // so templates that need to mirror the OpenAPI spec verbatim — e.g. echo's + // per-operation middleware map key — can do so without seeing the + // Go-identifier-friendly transformations applied below. + specOperationId := op.OperationID // We rely on OperationID to generate function names, it's required if operationId == "" { - operationId, err = generateDefaultOperationID(opName, requestPath, toCamelCaseFunc) + operationId, err = generateDefaultOperationID(opName, requestPath) if err != nil { return nil, fmt.Errorf("error generating default OperationID for %s/%s: %s", opName, requestPath, err) @@ -586,8 +1401,25 @@ func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]Oper } operationId = typeNamePrefix(operationId) + operationId - if !globalState.options.Compatibility.PreserveOriginalOperationIdCasingInEmbeddedSpec { - // update the existing, shared, copy of the spec if we're not wanting to preserve it + // Detect path aliases: when a path item has an internal $ref + // pointing to another path in the same document (e.g. + // "#/paths/~1test"), it's a duplicate that would produce + // identical server methods. External $refs (pointing to other + // files) are not aliases — they're the sole definition of + // that path, just stored externally. + isAlias := strings.HasPrefix(pathItem.Ref, "#/paths/") + var aliasTarget string + if isAlias { + aliasTarget = nameNormalizer(operationId) + n := aliasCounters[operationId] + aliasCounters[operationId] = n + 1 + operationId = operationId + fmt.Sprintf("Alias%d", n) + } + + if !globalState.options.Compatibility.PreserveOriginalOperationIdCasingInEmbeddedSpec && !isAlias { + // update the existing, shared, copy of the spec if we're not wanting to preserve it. + // Skip for aliases: they share the same *Operation as the canonical path, + // and writing the suffixed name back would corrupt the original. op.OperationID = operationId } @@ -616,14 +1448,14 @@ func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]Oper return nil, err } - bodyDefinitions, typeDefinitions, err := GenerateBodyDefinitions(operationId, op.RequestBody) + bodyDefinitions, typeDefinitions, err := GenerateBodyDefinitions(operationId, op.RequestBody, pathItem.Ref) if err != nil { return nil, fmt.Errorf("error generating body definitions: %w", err) } ensureExternalRefsInRequestBodyDefinitions(&bodyDefinitions, pathItem.Ref) - responseDefinitions, err := GenerateResponseDefinitions(operationId, op.Responses.Map()) + responseDefinitions, err := GenerateResponseDefinitions(operationId, op.Responses.Map(), pathItem.Ref) if err != nil { return nil, fmt.Errorf("error generating response definitions: %w", err) } @@ -631,19 +1463,24 @@ func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]Oper ensureExternalRefsInResponseDefinitions(&responseDefinitions, pathItem.Ref) opDef := OperationDefinition{ - PathParams: pathParams, - HeaderParams: FilterParameterDefinitionByType(allParams, "header"), - QueryParams: FilterParameterDefinitionByType(allParams, "query"), - CookieParams: FilterParameterDefinitionByType(allParams, "cookie"), - OperationId: nameNormalizer(operationId), + PathParams: pathParams, + HeaderParams: FilterParameterDefinitionByType(allParams, "header"), + QueryParams: FilterParameterDefinitionByType(allParams, "query"), + CookieParams: FilterParameterDefinitionByType(allParams, "cookie"), + OperationId: nameNormalizer(operationId), + SpecOperationId: specOperationId, // Replace newlines in summary. Summary: op.Summary, Method: opName, Path: requestPath, + SpecOrder: pathSpecOrder, Spec: op, Bodies: bodyDefinitions, Responses: responseDefinitions, TypeDefinitions: typeDefinitions, + IsAlias: isAlias, + AliasTarget: aliasTarget, + PathItemRef: pathItem.Ref, } // check for overrides of SecurityDefinitions. @@ -659,43 +1496,321 @@ func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]Oper opDef.SecurityDefinitions = DescribeSecurityDefinition(swagger.Security) } + opDef.SecurityDefinitions = filterOutUndefinedSecuritySchemes(opDef.SecurityDefinitions, definedSecuritySchemes) if op.RequestBody != nil { opDef.BodyRequired = op.RequestBody.Value.Required } + if op.Deprecated { + reason := "" + if extension, ok := op.Extensions[extDeprecationReason]; ok { + if r, err := extParseDeprecationReason(extension); err == nil { + reason = r + } + } + for i := range opDef.Bodies { + opDef.Bodies[i].Deprecated = true + opDef.Bodies[i].DeprecationReason = reason + } + } + // Generate all the type definitions needed for this operation opDef.TypeDefinitions = append(opDef.TypeDefinitions, GenerateTypeDefsForOperation(opDef)...) + // Declare the shared (path-item-level) parameter helper types once, + // on the first operation of the path item (issue #2090). + if !sharedTypeDefsEmitted { + opDef.TypeDefinitions = append(opDef.TypeDefinitions, sharedParamTypeDefs...) + sharedTypeDefsEmitted = true + } + operations = append(operations, opDef) } } return operations, nil } -func generateDefaultOperationID(opName string, requestPath string, toCamelCaseFunc func(string) string) (string, error) { - var operationId = strings.ToLower(opName) +// WebhookOperationDefinitions extracts OpenAPI 3.1+ webhook operations +// from swagger.Webhooks into the same OperationDefinition shape used for +// path operations, so they flow through the same downstream pipeline +// (body / response generation, type definitions, etc.) but are routed +// to webhook-specific templates. +// +// kin-openapi only populates the Webhooks field for OpenAPI 3.1+ +// documents, so a missing/empty map naturally short-circuits this for +// 3.0 specs without an explicit version check. +// +// The result mirrors OperationDefinitions in structure, minus the +// path-alias logic (webhooks have no path template) and the path- +// parameter extraction (webhooks have no path params). +func WebhookOperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) { + var operations []OperationDefinition + if swagger == nil || len(swagger.Webhooks) == 0 { + return operations, nil + } + + sharedParams, err := resolveSharedParameters(swagger) + if err != nil { + return nil, err + } + + for _, webhookName := range SortedMapKeys(swagger.Webhooks) { + pathItem := swagger.Webhooks[webhookName] + if pathItem == nil { + continue + } + + // Path-item-level parameters apply to every method on the webhook + // (rare for webhooks, but honored defensively). Their helper types are + // declared once for the path item (issue #2090). + globalParams := sharedParams[pathItem] + sharedParamTypeDefs := sharedParameterTypeDefs(globalParams) + sharedTypeDefsEmitted := false + + pathOps := pathItem.Operations() + for _, opName := range SortedMapKeys(pathOps) { + op := pathOps[opName] + + // Prefer an explicit operationId on the webhook operation; + // otherwise derive from the webhook map key. Either way, + // run through the configured name normalizer. + operationId := op.OperationID + if operationId == "" { + operationId = webhookName + } + operationId = nameNormalizer(operationId) + operationId = typeNamePrefix(operationId) + operationId + + localParams, err := DescribeParameters(op.Parameters, []string{operationId + "Params"}) + if err != nil { + return nil, fmt.Errorf("error describing webhook %q operation params: %w", webhookName, err) + } + allParams, err := CombineOperationParameters(globalParams, localParams) + if err != nil { + return nil, err + } + + bodyDefinitions, typeDefinitions, err := GenerateBodyDefinitions(operationId, op.RequestBody, pathItem.Ref) + if err != nil { + return nil, fmt.Errorf("error generating body definitions for webhook %q: %w", webhookName, err) + } + + responseDefinitions, err := GenerateResponseDefinitions(operationId, op.Responses.Map(), pathItem.Ref) + if err != nil { + return nil, fmt.Errorf("error generating response definitions for webhook %q: %w", webhookName, err) + } + + opDef := OperationDefinition{ + HeaderParams: FilterParameterDefinitionByType(allParams, "header"), + QueryParams: FilterParameterDefinitionByType(allParams, "query"), + CookieParams: FilterParameterDefinitionByType(allParams, "cookie"), + OperationId: nameNormalizer(operationId), + Summary: op.Summary, + Method: opName, + Path: "", + Spec: op, + Bodies: bodyDefinitions, + Responses: responseDefinitions, + TypeDefinitions: typeDefinitions, + IsWebhook: true, + WebhookName: webhookName, + } + + if op.Security != nil { + opDef.SecurityDefinitions = DescribeSecurityDefinition(*op.Security) + } else { + opDef.SecurityDefinitions = DescribeSecurityDefinition(swagger.Security) + } + + if op.RequestBody != nil { + opDef.BodyRequired = op.RequestBody.Value.Required + } + + opDef.TypeDefinitions = append(opDef.TypeDefinitions, GenerateTypeDefsForOperation(opDef)...) + if !sharedTypeDefsEmitted { + opDef.TypeDefinitions = append(opDef.TypeDefinitions, sharedParamTypeDefs...) + sharedTypeDefsEmitted = true + } + operations = append(operations, opDef) + } + } + return operations, nil +} + +// CallbackOperationDefinitions extracts OpenAPI callback operations +// from spec.Paths...Callbacks into the same +// OperationDefinition shape used for path operations, so they flow +// through the same downstream pipeline (body / response generation, +// type definitions, etc.) but are routed to callback-specific +// templates. +// +// Callbacks have been part of the OpenAPI spec since 3.0, so this is +// not gated on version: any spec that declares callbacks gets them +// generated. The spec shape: +// +// paths: +// /api/plant_tree: +// post: +// operationId: PlantTree +// callbacks: +// treePlanted: # the callback map key +// '{$request.body#/url}': # the runtime URL expression +// post: +// operationId: TreePlanted +// +// Each leaf operation (the inner `post:` above) becomes one +// OperationDefinition with IsCallback=true and CallbackName set to the +// outer map key ("treePlanted"). The codegen does not interpret the URL +// expression itself -- the caller of the generated CallbackInitiator +// supplies the resolved target URL at runtime (the same way it would +// for a webhook). +func CallbackOperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) { + var operations []OperationDefinition + if swagger == nil || swagger.Paths == nil { + return operations, nil + } + + sharedParams, err := resolveSharedParameters(swagger) + if err != nil { + return nil, err + } + + for _, requestPath := range SortedMapKeys(swagger.Paths.Map()) { + pathItem := swagger.Paths.Value(requestPath) + if pathItem == nil { + continue + } + // Iterate path-item operations in sorted method order for + // deterministic output across runs (Operations() returns a + // map; range order is randomized). + parentOps := pathItem.Operations() + for _, parentMethod := range SortedMapKeys(parentOps) { + parentOp := parentOps[parentMethod] + if len(parentOp.Callbacks) == 0 { + continue + } + for _, callbackName := range SortedMapKeys(parentOp.Callbacks) { + cbRef := parentOp.Callbacks[callbackName] + if cbRef == nil || cbRef.Value == nil { + continue + } + cb := cbRef.Value + // A Callback maps URL-expression to PathItem; iterate + // in sorted key order for deterministic output. The + // internal map is private so use the accessor pair. + cbKeys := append([]string(nil), cb.Keys()...) + slices.Sort(cbKeys) + for _, urlExpr := range cbKeys { + cbPathItem := cb.Value(urlExpr) + if cbPathItem == nil { + continue + } + // Path-item-level parameters shared by every method on + // the callback path item; helper types declared once + // (issue #2090). + globalParams := sharedParams[cbPathItem] + sharedParamTypeDefs := sharedParameterTypeDefs(globalParams) + sharedTypeDefsEmitted := false + + cbOps := cbPathItem.Operations() + for _, opName := range SortedMapKeys(cbOps) { + op := cbOps[opName] + + operationId := op.OperationID + if operationId == "" { + operationId = callbackName + } + operationId = nameNormalizer(operationId) + operationId = typeNamePrefix(operationId) + operationId + + localParams, err := DescribeParameters(op.Parameters, []string{operationId + "Params"}) + if err != nil { + return nil, fmt.Errorf("error describing callback %q operation params: %w", callbackName, err) + } + allParams, err := CombineOperationParameters(globalParams, localParams) + if err != nil { + return nil, err + } + + bodyDefinitions, typeDefinitions, err := GenerateBodyDefinitions(operationId, op.RequestBody, cbPathItem.Ref) + if err != nil { + return nil, fmt.Errorf("error generating body definitions for callback %q: %w", callbackName, err) + } + + responseDefinitions, err := GenerateResponseDefinitions(operationId, op.Responses.Map(), cbPathItem.Ref) + if err != nil { + return nil, fmt.Errorf("error generating response definitions for callback %q: %w", callbackName, err) + } + + opDef := OperationDefinition{ + HeaderParams: FilterParameterDefinitionByType(allParams, "header"), + QueryParams: FilterParameterDefinitionByType(allParams, "query"), + CookieParams: FilterParameterDefinitionByType(allParams, "cookie"), + OperationId: nameNormalizer(operationId), + Summary: op.Summary, + Method: opName, + Path: "", + Spec: op, + Bodies: bodyDefinitions, + Responses: responseDefinitions, + TypeDefinitions: typeDefinitions, + IsCallback: true, + CallbackName: callbackName, + } + + if op.Security != nil { + opDef.SecurityDefinitions = DescribeSecurityDefinition(*op.Security) + } else { + opDef.SecurityDefinitions = DescribeSecurityDefinition(swagger.Security) + } + + if op.RequestBody != nil { + opDef.BodyRequired = op.RequestBody.Value.Required + } + + opDef.TypeDefinitions = append(opDef.TypeDefinitions, GenerateTypeDefsForOperation(opDef)...) + if !sharedTypeDefsEmitted { + opDef.TypeDefinitions = append(opDef.TypeDefinitions, sharedParamTypeDefs...) + sharedTypeDefsEmitted = true + } + operations = append(operations, opDef) + } + } + } + } + } + return operations, nil +} +func generateDefaultOperationID(opName string, requestPath string) (string, error) { if opName == "" { return "", fmt.Errorf("operation name cannot be an empty string") } - if requestPath == "" { return "", fmt.Errorf("request path cannot be an empty string") } - for _, part := range strings.Split(requestPath, "/") { + operationID := strings.ToLower(opName) + for part := range strings.SplitSeq(requestPath, "/") { if part != "" { - operationId = operationId + "-" + part + operationID = operationID + "-" + part } } - return nameNormalizer(operationId), nil + return nameNormalizer(operationID), nil } // GenerateBodyDefinitions turns the Swagger body definitions into a list of our body // definitions which will be used for code generation. -func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBodyRef) ([]RequestBodyDefinition, []TypeDefinition, error) { +// +// pathItemRef is the path item's $ref (if any). When non-empty and pointing at +// an external file, the body type that would otherwise be hoisted locally is +// replaced by a reference to the imported package's same-named type — the +// imported package already declares it (with any As/From/Merge methods), so +// redeclaring locally would just produce an awkward duplicate with +// package-qualified union elements. +func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBodyRef, pathItemRef string) ([]RequestBodyDefinition, []TypeDefinition, error) { if bodyOrRef == nil { return nil, nil, nil } @@ -750,24 +1865,31 @@ func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBody // type under #/components, we'll define a type for it, so // that we have an easy to use type for marshaling. if bodySchema.RefType == "" { - if contentType == "application/x-www-form-urlencoded" { - // Apply the appropriate structure tag if the request - // schema was defined under the operations' section. - for i := range bodySchema.Properties { - bodySchema.Properties[i].NeedsFormTag = true - } + if externalPkg := externalPackageFor(pathItemRef); externalPkg != "" { + // The operation's path item came from an external file; the + // imported package already declares this body type with the + // matching name. Reference it instead of redeclaring. + bodySchema.RefType = fmt.Sprintf("%s.%s", externalPkg, bodyTypeName) + } else { + if contentType == "application/x-www-form-urlencoded" { + // Apply the appropriate structure tag if the request + // schema was defined under the operations' section. + for i := range bodySchema.Properties { + bodySchema.Properties[i].NeedsFormTag = true + } - // Regenerate the Golang struct adding the new form tag. - bodySchema.GoType = GenStructFromSchema(bodySchema) - } + // Regenerate the Golang struct adding the new form tag. + bodySchema.GoType = GenStructFromSchema(bodySchema) + } - td := TypeDefinition{ - TypeName: bodyTypeName, - Schema: bodySchema, + td := TypeDefinition{ + TypeName: bodyTypeName, + Schema: bodySchema, + } + typeDefinitions = append(typeDefinitions, td) + // The body schema now is a reference to a type + bodySchema.RefType = bodyTypeName } - typeDefinitions = append(typeDefinitions, td) - // The body schema now is a reference to a type - bodySchema.RefType = bodyTypeName } bd := RequestBodyDefinition{ @@ -779,7 +1901,7 @@ func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBody } if len(content.Encoding) != 0 { - bd.Encoding = make(map[string]RequestBodyEncoding) + bd.Encoding = make(map[string]RequestBodyEncoding, len(content.Encoding)) for k, v := range content.Encoding { encoding := RequestBodyEncoding{ContentType: v.ContentType, Style: v.Style, Explode: v.Explode} bd.Encoding[k] = encoding @@ -788,13 +1910,15 @@ func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBody bodyDefinitions = append(bodyDefinitions, bd) } - sort.Slice(bodyDefinitions, func(i, j int) bool { - return bodyDefinitions[i].ContentType < bodyDefinitions[j].ContentType + slices.SortFunc(bodyDefinitions, func(a, b RequestBodyDefinition) int { + return cmp.Compare(a.ContentType, b.ContentType) }) return bodyDefinitions, typeDefinitions, nil } -func GenerateResponseDefinitions(operationID string, responses map[string]*openapi3.ResponseRef) ([]ResponseDefinition, error) { +func GenerateResponseDefinitions(operationID string, responses map[string]*openapi3.ResponseRef, pathItemRef string) ([]ResponseDefinition, error) { + externalPkg := externalPackageFor(pathItemRef) + var responseDefinitions []ResponseDefinition // do not let multiple status codes ref to same response, it will break the type switch refSet := make(map[string]struct{}) @@ -831,11 +1955,45 @@ func GenerateResponseDefinitions(operationID string, responses map[string]*opena } responseTypeName := operationID + statusCode + tag + "Response" - contentSchema, err := GenerateGoSchema(content.Schema, []string{responseTypeName}) + // The strict-server envelope keeps the bare ...Response name + // (e.g. "GetPing200JSONResponse"); the hoisted body type is + // suffixed so the envelope can reference it without colliding + // (the strict envelope is sometimes a struct that wraps the + // body in a Body field, which would self-reference if the + // names matched). + responseBodyTypeName := responseTypeName + "Body" + contentSchema, err := GenerateGoSchema(content.Schema, []string{responseBodyTypeName}) if err != nil { return nil, fmt.Errorf("error generating request body definition: %w", err) } + // Hoist inline response-root schemas that need method-emitting + // boilerplate (UnionElements / AdditionalProperties) to a + // synthetic top-level TypeDefinition. The hoisted typedef flows + // via op.TypeDefinitions (collected in + // GenerateTypeDefsForOperation) and gets declared once via + // typedef.tmpl with full union/additionalProperties methods. + // The strict-server template references it as the body type + // from the envelope. + // + // When the operation came from an externally-ref'd path item, + // the imported package generated the same hoisted name, so we + // reference it instead of redeclaring locally. + if !IsGoTypeReference(responseOrRef.Ref) && contentSchema.RefType == "" && + (len(contentSchema.UnionElements) != 0 || contentSchema.HasAdditionalProperties || + (globalState.options.OutputOptions.GenerateTypesForAnonymousSchemas && len(contentSchema.Properties) > 0)) { + if externalPkg != "" { + contentSchema.RefType = fmt.Sprintf("%s.%s", externalPkg, responseBodyTypeName) + } else { + contentSchema.AdditionalTypes = append(contentSchema.AdditionalTypes, TypeDefinition{ + TypeName: responseBodyTypeName, + JsonName: responseBodyTypeName, + Schema: contentSchema, + }) + contentSchema.RefType = responseBodyTypeName + } + } + rcd := ResponseContentDefinition{ ContentType: contentType, NameTag: tag, @@ -852,7 +2010,39 @@ func GenerateResponseDefinitions(operationID string, responses map[string]*opena if err != nil { return nil, fmt.Errorf("error generating response header definition: %w", err) } - headerDefinition := ResponseHeaderDefinition{Name: headerName, GoName: SchemaNameToTypeName(headerName), Schema: contentSchema} + // When the header component itself is an external `$ref` (it lives + // in another file) and its schema references a named type, that + // type is generated into the imported package, not this one. The + // schema `$ref` is written relative to the external file (e.g. + // "#/components/schemas/ETagSchema"), so GenerateGoSchema resolved + // it as a bare local name; qualify it with the header's external + // package so we emit "externalRef0.ETagSchema" instead of an + // undefined local "ETagSchema" (issue #2060). Guard on the schema + // being a reference so an external header with an inline primitive + // schema (e.g. `type: string`) is not turned into + // "externalRef0.string". + if header.Value.Schema != nil && header.Value.Schema.Ref != "" { + ensureExternalRefsInSchema(&contentSchema, header.Ref) + } + var nullable bool + if header.Value.Schema != nil { + nullable = schemaIsNullable(header.Value.Schema.Value) + } + headerDefinition := ResponseHeaderDefinition{ + Name: headerName, + GoName: SchemaNameToTypeName(headerName), + Schema: contentSchema, + Required: header.Value.Required || globalState.options.Compatibility.HeadersImplicitlyRequired, + Nullable: nullable, + } + if header.Value.Deprecated { + headerDefinition.Deprecated = true + if extension, ok := header.Value.Extensions[extDeprecationReason]; ok { + if r, err := extParseDeprecationReason(extension); err == nil { + headerDefinition.DeprecationReason = r + } + } + } responseHeaderDefinitions = append(responseHeaderDefinitions, headerDefinition) } @@ -865,10 +2055,27 @@ func GenerateResponseDefinitions(operationID string, responses map[string]*opena rd.Description = *response.Description } if IsGoTypeReference(responseOrRef.Ref) { - // Convert the reference path to Go type - refType, err := RefPathToGoType(responseOrRef.Ref) - if err != nil { - return nil, fmt.Errorf("error turning reference (%s) into a Go type: %w", responseOrRef.Ref, err) + var refType string + if externalPkg != "" && strings.HasPrefix(responseOrRef.Ref, "#") { + // The operation came from an externally-ref'd path item and this + // response ref is relative to that external file, so the + // referenced response actually lives in the imported package + // (issue #2308). Resolve the name in the context of the external + // document: derive it straight from the ref's component name + // rather than via RefPathToGoType, which resolves "#" refs + // against the *root* spec and would apply the root spec's + // collision resolution / x-go-name -- a name the imported + // package never generated. Refs that already target another file + // (handled below) are qualified by RefPathToGoType itself. + refParts := strings.Split(responseOrRef.Ref, "/") + refType = fmt.Sprintf("%s.%s", externalPkg, SchemaNameToTypeName(refParts[len(refParts)-1])) + } else { + // Convert the reference path to Go type + var err error + refType, err = RefPathToGoType(responseOrRef.Ref) + if err != nil { + return nil, fmt.Errorf("error turning reference (%s) into a Go type: %w", responseOrRef.Ref, err) + } } // Check if this ref is already used by another response definition. If not use the ref // If we let multiple response definitions alias to same response it will break the type switch @@ -877,6 +2084,12 @@ func GenerateResponseDefinitions(operationID string, responses map[string]*opena rd.Ref = refType refSet[refType] = struct{}{} } + // Ensure content schemas get the external ref qualifier so that + // non-fixed status code paths (e.g. "default") emit the qualified type. + for i, rcd := range rd.Contents { + ensureExternalRefsInSchema(&rcd.Schema, responseOrRef.Ref) + rd.Contents[i] = rcd + } } responseDefinitions = append(responseDefinitions, rd) } @@ -891,14 +2104,26 @@ func GenerateTypeDefsForOperation(op OperationDefinition) []TypeDefinition { typeDefs = append(typeDefs, GenerateParamsTypes(op)...) } - // Now, go through all the additional types we need to declare. + // Now, go through all the additional types we need to declare. Skip + // parameters shared at the path-item level: their helper types are + // declared once for the path item (see sharedParameterTypeDefs), not once + // per operation, which would redeclare them (issue #2090). for _, param := range op.AllParams() { + if param.Shared { + continue + } typeDefs = append(typeDefs, param.Schema.AdditionalTypes...) } for _, body := range op.Bodies { typeDefs = append(typeDefs, body.Schema.AdditionalTypes...) } + + for _, resp := range op.Responses { + for _, content := range resp.Contents { + typeDefs = append(typeDefs, content.Schema.AdditionalTypes...) + } + } return typeDefs } @@ -925,18 +2150,28 @@ func GenerateParamsTypes(op OperationDefinition) []TypeDefinition { Schema: param.Schema, }) } + // Merge extensions, in order of increasing precedence: + // 1. extensions on the referenced schema (param.Spec.Schema.Value) + // 2. extensions placed as siblings of a $ref inside the + // parameter's schema (param.Spec.Schema.Extensions) + // 3. extensions on the Parameter object itself + extensions := make(map[string]any) + if param.Spec.Schema != nil { + maps.Copy(extensions, combinedSchemaExtensions(param.Spec.Schema)) + } + maps.Copy(extensions, param.Spec.Extensions) prop := Property{ Description: param.Spec.Description, JsonFieldName: param.ParamName, Required: param.Required, Schema: pSchema, NeedsFormTag: param.Style() == "form", - Extensions: param.Spec.Extensions, + Extensions: extensions, + Deprecated: param.Spec.Deprecated, } s.Properties = append(s.Properties, prop) } - s.Description = op.Spec.Description s.GoType = GenStructFromSchema(s) td := TypeDefinition{ @@ -959,72 +2194,181 @@ func GenerateTypesForOperations(t *template.Template, ops []OperationDefinition) return "", fmt.Errorf("error writing boilerplate to buffer: %w", err) } - // Generate boiler plate for all additional types. - var td []TypeDefinition - for _, op := range ops { - td = append(td, op.TypeDefinitions...) + if err = w.Flush(); err != nil { + return "", fmt.Errorf("error flushing output buffer for server interface: %w", err) } - addProps, err := GenerateAdditionalPropertyBoilerplate(t, td) - if err != nil { - return "", fmt.Errorf("error generating additional properties boilerplate for operations: %w", err) - } + return buf.String(), nil +} - if _, err := w.WriteString("\n"); err != nil { - return "", fmt.Errorf("error generating additional properties boilerplate for operations: %w", err) - } +// sortOperationsBySpecOrder returns a copy of ops reordered to follow the +// order paths are declared in the spec (by source line, OperationDefinition +// .SpecOrder), with a stable fallback to the incoming order when source +// locations are unavailable (e.g. an in-memory spec) or shared by several +// operations (the methods of one path). This is used only for route +// registration (issue #1887); the rest of the pipeline keeps the sorted +// order so name-collision resolution and type emission are unaffected. +func sortOperationsBySpecOrder(ops []OperationDefinition) []OperationDefinition { + out := make([]OperationDefinition, len(ops)) + copy(out, ops) + slices.SortStableFunc(out, func(a, b OperationDefinition) int { + return a.SpecOrder - b.SpecOrder + }) + return out +} - if _, err := w.WriteString(addProps); err != nil { - return "", fmt.Errorf("error generating additional properties boilerplate for operations: %w", err) - } +// sortOperationsLexicographically returns a copy of ops sorted by path then +// method, reproducing the historical (pre-#1887) route-registration order in +// which OperationDefinitions gathers paths. It is a stable no-op on an +// already-gathered slice, but re-sorts explicitly so the ordering does not +// depend on the caller's input. +func sortOperationsLexicographically(ops []OperationDefinition) []OperationDefinition { + out := make([]OperationDefinition, len(ops)) + copy(out, ops) + slices.SortStableFunc(out, func(a, b OperationDefinition) int { + if c := cmp.Compare(a.Path, b.Path); c != 0 { + return c + } + return cmp.Compare(a.Method, b.Method) + }) + return out +} - if err = w.Flush(); err != nil { - return "", fmt.Errorf("error flushing output buffer for server interface: %w", err) +// operationsInRegistrationOrder returns ops in the order route handlers should +// be registered. By default this follows spec-declaration order (issue #1887); +// when the sort-handler-registrations compatibility flag is set it restores the +// historical lexicographic order. +func operationsInRegistrationOrder(ops []OperationDefinition) []OperationDefinition { + if globalState.options.Compatibility.SortHandlerRegistrations { + return sortOperationsLexicographically(ops) } - - return buf.String(), nil + return sortOperationsBySpecOrder(ops) } // GenerateIrisServer generates all the go code for the ServerInterface as well as // all the wrapper functions around our handlers. func GenerateIrisServer(t *template.Template, operations []OperationDefinition) (string, error) { - return GenerateTemplates([]string{"iris/iris-interface.tmpl", "iris/iris-middleware.tmpl", "iris/iris-handler.tmpl"}, t, operations) + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, []string{"iris/iris-interface.tmpl", "iris/iris-middleware.tmpl"}, t, operations); err != nil { + return "", err + } + // Route registration follows spec-declaration order (issue #1887). + if err := GenerateTemplatesIntoBuffer(&buf, []string{"iris/iris-handler.tmpl"}, t, operationsInRegistrationOrder(operations)); err != nil { + return "", err + } + return buf.String(), nil } // GenerateChiServer generates all the go code for the ServerInterface as well as // all the wrapper functions around our handlers. func GenerateChiServer(t *template.Template, operations []OperationDefinition) (string, error) { - return GenerateTemplates([]string{"chi/chi-interface.tmpl", "chi/chi-middleware.tmpl", "chi/chi-handler.tmpl"}, t, operations) + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, []string{"chi/chi-interface.tmpl", "chi/chi-middleware.tmpl"}, t, operations); err != nil { + return "", err + } + // Route registration follows spec-declaration order (issue #1887). + if err := GenerateTemplatesIntoBuffer(&buf, []string{"chi/chi-handler.tmpl"}, t, operationsInRegistrationOrder(operations)); err != nil { + return "", err + } + return buf.String(), nil } // GenerateFiberServer generates all the go code for the ServerInterface as well as // all the wrapper functions around our handlers. func GenerateFiberServer(t *template.Template, operations []OperationDefinition) (string, error) { - return GenerateTemplates([]string{"fiber/fiber-interface.tmpl", "fiber/fiber-middleware.tmpl", "fiber/fiber-handler.tmpl"}, t, operations) + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, []string{"fiber/fiber-interface.tmpl", "fiber/fiber-middleware.tmpl"}, t, operations); err != nil { + return "", err + } + // Route registration follows spec-declaration order (issue #1887). + if err := GenerateTemplatesIntoBuffer(&buf, []string{"fiber/fiber-handler.tmpl"}, t, operationsInRegistrationOrder(operations)); err != nil { + return "", err + } + return buf.String(), nil +} + +// GenerateFiberV3Server generates all the go code for the ServerInterface as well as +// all the wrapper functions around our handlers. +func GenerateFiberV3Server(t *template.Template, operations []OperationDefinition) (string, error) { + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, []string{"fiber-v3/fiber-interface.tmpl", "fiber-v3/fiber-middleware.tmpl"}, t, operations); err != nil { + return "", err + } + // Route registration follows spec-declaration order (issue #1887). + if err := GenerateTemplatesIntoBuffer(&buf, []string{"fiber-v3/fiber-handler.tmpl"}, t, operationsInRegistrationOrder(operations)); err != nil { + return "", err + } + return buf.String(), nil } // GenerateEchoServer generates all the go code for the ServerInterface as well as // all the wrapper functions around our handlers. func GenerateEchoServer(t *template.Template, operations []OperationDefinition) (string, error) { - return GenerateTemplates([]string{"echo/echo-interface.tmpl", "echo/echo-wrappers.tmpl", "echo/echo-register.tmpl"}, t, operations) + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, []string{"echo/echo-interface.tmpl", "echo/echo-wrappers.tmpl"}, t, operations); err != nil { + return "", err + } + // Route registration follows spec-declaration order (issue #1887). + if err := GenerateTemplatesIntoBuffer(&buf, []string{"echo/echo-register.tmpl"}, t, operationsInRegistrationOrder(operations)); err != nil { + return "", err + } + return buf.String(), nil +} + +// GenerateEcho5Server generates all the go code for the ServerInterface as well as +// all the wrapper functions around our handlers. +func GenerateEcho5Server(t *template.Template, operations []OperationDefinition) (string, error) { + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, []string{"echo/v5/echo-interface.tmpl", "echo/v5/echo-wrappers.tmpl"}, t, operations); err != nil { + return "", err + } + // Route registration follows spec-declaration order (issue #1887). + if err := GenerateTemplatesIntoBuffer(&buf, []string{"echo/v5/echo-register.tmpl"}, t, operationsInRegistrationOrder(operations)); err != nil { + return "", err + } + return buf.String(), nil } // GenerateGinServer generates all the go code for the ServerInterface as well as // all the wrapper functions around our handlers. func GenerateGinServer(t *template.Template, operations []OperationDefinition) (string, error) { - return GenerateTemplates([]string{"gin/gin-interface.tmpl", "gin/gin-wrappers.tmpl", "gin/gin-register.tmpl"}, t, operations) + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, []string{"gin/gin-interface.tmpl", "gin/gin-wrappers.tmpl"}, t, operations); err != nil { + return "", err + } + // Route registration follows spec-declaration order (issue #1887). + if err := GenerateTemplatesIntoBuffer(&buf, []string{"gin/gin-register.tmpl"}, t, operationsInRegistrationOrder(operations)); err != nil { + return "", err + } + return buf.String(), nil } // GenerateGorillaServer generates all the go code for the ServerInterface as well as // all the wrapper functions around our handlers. func GenerateGorillaServer(t *template.Template, operations []OperationDefinition) (string, error) { - return GenerateTemplates([]string{"gorilla/gorilla-interface.tmpl", "gorilla/gorilla-middleware.tmpl", "gorilla/gorilla-register.tmpl"}, t, operations) + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, []string{"gorilla/gorilla-interface.tmpl", "gorilla/gorilla-middleware.tmpl"}, t, operations); err != nil { + return "", err + } + // Route registration follows spec-declaration order (issue #1887). + if err := GenerateTemplatesIntoBuffer(&buf, []string{"gorilla/gorilla-register.tmpl"}, t, operationsInRegistrationOrder(operations)); err != nil { + return "", err + } + return buf.String(), nil } // GenerateStdHTTPServer generates all the go code for the ServerInterface as well as // all the wrapper functions around our handlers. func GenerateStdHTTPServer(t *template.Template, operations []OperationDefinition) (string, error) { - return GenerateTemplates([]string{"stdhttp/std-http-interface.tmpl", "stdhttp/std-http-middleware.tmpl", "stdhttp/std-http-handler.tmpl"}, t, operations) + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, []string{"stdhttp/std-http-interface.tmpl", "stdhttp/std-http-middleware.tmpl"}, t, operations); err != nil { + return "", err + } + // Route registration follows spec-declaration order (issue #1887). + if err := GenerateTemplatesIntoBuffer(&buf, []string{"stdhttp/std-http-handler.tmpl"}, t, operationsInRegistrationOrder(operations)); err != nil { + return "", err + } + return buf.String(), nil } func GenerateStrictServer(t *template.Template, operations []OperationDefinition, opts Configuration) (string, error) { @@ -1043,9 +2387,15 @@ func GenerateStrictServer(t *template.Template, operations []OperationDefinition if opts.Generate.FiberServer { templates = append(templates, "strict/strict-fiber-interface.tmpl", "strict/strict-fiber.tmpl") } + if opts.Generate.FiberV3Server { + templates = append(templates, "strict/strict-fiber-v3-interface.tmpl", "strict/strict-fiber-v3.tmpl") + } if opts.Generate.IrisServer { templates = append(templates, "strict/strict-iris-interface.tmpl", "strict/strict-iris.tmpl") } + if opts.Generate.Echo5Server { + templates = append(templates, "strict/strict-interface.tmpl", "strict/strict-echo5.tmpl") + } return GenerateTemplates(templates, t, operations) } @@ -1066,23 +2416,125 @@ func GenerateClientWithResponses(t *template.Template, ops []OperationDefinition return GenerateTemplates([]string{"client-with-responses.tmpl"}, t, ops) } -// GenerateTemplates used to generate templates -func GenerateTemplates(templates []string, t *template.Template, ops interface{}) (string, error) { - var generatedTemplates []string - for _, tmpl := range templates { - var buf bytes.Buffer - w := bufio.NewWriter(&buf) - - if err := t.ExecuteTemplate(w, tmpl, ops); err != nil { - return "", fmt.Errorf("error generating %s: %s", tmpl, err) +// GenerateWebhookInitiator generates the WebhookInitiator -- the +// client-side analog for OpenAPI 3.1 webhooks. It mirrors the path +// Client (struct + options + per-method calls + request builders) but +// takes the target URL per-call instead of from a stored Server field. +// The caller passes only the webhook OperationDefinitions (gathered via +// WebhookOperationDefinitions); path operations are emitted by the +// regular Client templates. +func GenerateWebhookInitiator(t *template.Template, webhookOps []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"webhook-initiator.tmpl"}, t, webhookOps) +} + +// GenerateCallbackInitiator generates the CallbackInitiator -- the +// client-side analog for OpenAPI callbacks. Structurally identical to +// GenerateWebhookInitiator but takes the callback OperationDefinitions +// gathered via CallbackOperationDefinitions, which walk paths/operations/ +// callbacks rather than spec.Webhooks. +func GenerateCallbackInitiator(t *template.Template, callbackOps []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"callback-initiator.tmpl"}, t, callbackOps) +} + +// GenerateStdHTTPReceiver renders the merged stdhttp receiver template +// (used for both webhook and callback receivers). The caller selects +// between them by passing prefix "Webhook" or "Callback" along with the +// matching OperationDefinitions. The template emits a {Prefix}Receiver +// interface plus per-operation {Op}{Prefix}Handler factories with +// query/header parameter binding inline (matching the param-binding +// machinery used by the path-server middleware). +func GenerateStdHTTPReceiver(t *template.Template, prefix string, ops []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"stdhttp/std-http-receiver.tmpl"}, t, NewReceiverTemplateData(prefix, ops)) +} + +// GenerateChiReceiver renders the chi receiver template. Chi shares +// stdhttp's (w, r) handler signature, so the template is structurally +// identical -- only the file path and (in the future, if needed) +// framework-specific helpers differ. +func GenerateChiReceiver(t *template.Template, prefix string, ops []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"chi/chi-receiver.tmpl"}, t, NewReceiverTemplateData(prefix, ops)) +} + +// GenerateGorillaReceiver renders the gorilla/mux receiver template. +// Gorilla shares stdhttp's (w, r) handler signature, so the template +// is structurally identical to stdhttp's. +func GenerateGorillaReceiver(t *template.Template, prefix string, ops []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"gorilla/gorilla-receiver.tmpl"}, t, NewReceiverTemplateData(prefix, ops)) +} + +// GenerateEchoReceiver renders the echo (v4) receiver template. Echo's +// handler shape is `(ctx echo.Context) error`, and binding errors are +// returned via echo.NewHTTPError so echo's framework error chain +// reports them as 400 -- there's no errHandler argument like the +// stdhttp receiver factory has. +func GenerateEchoReceiver(t *template.Template, prefix string, ops []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"echo/echo-receiver.tmpl"}, t, NewReceiverTemplateData(prefix, ops)) +} + +// GenerateEcho5Receiver renders the echo (v5) receiver template. Same +// shape as v4 but with `*echo.Context` (pointer) -- the only API +// difference between echo v4 and v5 that affects the receiver. +func GenerateEcho5Receiver(t *template.Template, prefix string, ops []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"echo/v5/echo-receiver.tmpl"}, t, NewReceiverTemplateData(prefix, ops)) +} + +// GenerateGinReceiver renders the gin receiver template. Gin's handler +// shape is `(c *gin.Context)` (no error return); binding errors abort +// with c.JSON(400, gin.H{"error": ...}). Per-handler middleware is not +// generated here -- gin's idiom prefers engine .Use() composition. +func GenerateGinReceiver(t *template.Template, prefix string, ops []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"gin/gin-receiver.tmpl"}, t, NewReceiverTemplateData(prefix, ops)) +} + +// GenerateFiberReceiver renders the fiber (v2) receiver template. +// Fiber's handler shape is `(c *fiber.Ctx) error`; binding errors are +// returned via fiber.NewError so fiber's error chain reports them as +// 400. Per-handler middleware is not generated; use fiber.App.Use(). +func GenerateFiberReceiver(t *template.Template, prefix string, ops []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"fiber/fiber-receiver.tmpl"}, t, NewReceiverTemplateData(prefix, ops)) +} + +// GenerateFiberV3Receiver renders the fiber (v3) receiver template. +// Same shape as v2 but with `fiber.Ctx` (interface, by value) -- the +// only API difference between fiber v2 and v3 that affects the +// receiver. +func GenerateFiberV3Receiver(t *template.Template, prefix string, ops []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"fiber-v3/fiber-receiver.tmpl"}, t, NewReceiverTemplateData(prefix, ops)) +} + +// GenerateIrisReceiver renders the iris receiver template. Iris's +// handler shape is `(ctx iris.Context)` (no error return); binding +// errors set ctx.StatusCode(400) plus ctx.WriteString and return. +// Per-handler middleware is not generated; use app.Use() at the +// engine or Party level. +func GenerateIrisReceiver(t *template.Template, prefix string, ops []OperationDefinition) (string, error) { + return GenerateTemplates([]string{"iris/iris-receiver.tmpl"}, t, NewReceiverTemplateData(prefix, ops)) +} + +// GenerateTemplatesIntoBuffer executes the named templates against ops and +// writes the results to buf, separating consecutive templates with a newline. +// Rendering into a caller-owned buffer lets a caller compose several passes — +// e.g. feeding spec-ordered operations to the registration template while the +// rest of a server uses the pipeline's default order (issue #1887). +func GenerateTemplatesIntoBuffer(buf *bytes.Buffer, templates []string, t *template.Template, ops any) error { + for i, tmpl := range templates { + if i > 0 { + buf.WriteString("\n") } - if err := w.Flush(); err != nil { - return "", fmt.Errorf("error flushing output buffer for %s: %s", tmpl, err) + if err := t.ExecuteTemplate(buf, tmpl, ops); err != nil { + return fmt.Errorf("error generating %s: %s", tmpl, err) } - generatedTemplates = append(generatedTemplates, buf.String()) } + return nil +} - return strings.Join(generatedTemplates, "\n"), nil +// GenerateTemplates used to generate templates +func GenerateTemplates(templates []string, t *template.Template, ops any) (string, error) { + var buf bytes.Buffer + if err := GenerateTemplatesIntoBuffer(&buf, templates, t, ops); err != nil { + return "", err + } + return buf.String(), nil } // CombineOperationParameters combines the Parameters defined at a global level (Parameters defined for all methods on a given path) with the Parameters defined at a local level (Parameters defined for a specific path), preferring the locally defined parameter over the global one diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/prune.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/prune.go index 0d3a88974..efdac65f0 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/prune.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/prune.go @@ -2,23 +2,15 @@ package codegen import ( "fmt" + "slices" "github.com/getkin/kin-openapi/openapi3" ) -func stringInSlice(a string, list []string) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - type RefWrapper struct { Ref string HasValue bool - SourceRef interface{} + SourceRef any } func walkSwagger(swagger *openapi3.T, doFn func(RefWrapper) (bool, error)) error { @@ -402,7 +394,7 @@ func removeOrphanedComponents(swagger *openapi3.T, refs []string) int { for key := range swagger.Components.Schemas { ref := fmt.Sprintf("#/components/schemas/%s", key) - if !stringInSlice(ref, refs) { + if !slices.Contains(refs, ref) { countRemoved++ delete(swagger.Components.Schemas, key) } @@ -410,7 +402,7 @@ func removeOrphanedComponents(swagger *openapi3.T, refs []string) int { for key := range swagger.Components.Parameters { ref := fmt.Sprintf("#/components/parameters/%s", key) - if !stringInSlice(ref, refs) { + if !slices.Contains(refs, ref) { countRemoved++ delete(swagger.Components.Parameters, key) } @@ -421,7 +413,7 @@ func removeOrphanedComponents(swagger *openapi3.T, refs []string) int { // for key, _ := range swagger.Components.SecuritySchemes { // ref := fmt.Sprintf("#/components/securitySchemes/%s", key) - // if !stringInSlice(ref, refs) { + // if !slices.Contains(refs, ref) { // countRemoved++ // delete(swagger.Components.SecuritySchemes, key) // } @@ -429,7 +421,7 @@ func removeOrphanedComponents(swagger *openapi3.T, refs []string) int { for key := range swagger.Components.RequestBodies { ref := fmt.Sprintf("#/components/requestBodies/%s", key) - if !stringInSlice(ref, refs) { + if !slices.Contains(refs, ref) { countRemoved++ delete(swagger.Components.RequestBodies, key) } @@ -437,7 +429,7 @@ func removeOrphanedComponents(swagger *openapi3.T, refs []string) int { for key := range swagger.Components.Responses { ref := fmt.Sprintf("#/components/responses/%s", key) - if !stringInSlice(ref, refs) { + if !slices.Contains(refs, ref) { countRemoved++ delete(swagger.Components.Responses, key) } @@ -445,7 +437,7 @@ func removeOrphanedComponents(swagger *openapi3.T, refs []string) int { for key := range swagger.Components.Headers { ref := fmt.Sprintf("#/components/headers/%s", key) - if !stringInSlice(ref, refs) { + if !slices.Contains(refs, ref) { countRemoved++ delete(swagger.Components.Headers, key) } @@ -453,7 +445,7 @@ func removeOrphanedComponents(swagger *openapi3.T, refs []string) int { for key := range swagger.Components.Examples { ref := fmt.Sprintf("#/components/examples/%s", key) - if !stringInSlice(ref, refs) { + if !slices.Contains(refs, ref) { countRemoved++ delete(swagger.Components.Examples, key) } @@ -461,7 +453,7 @@ func removeOrphanedComponents(swagger *openapi3.T, refs []string) int { for key := range swagger.Components.Links { ref := fmt.Sprintf("#/components/links/%s", key) - if !stringInSlice(ref, refs) { + if !slices.Contains(refs, ref) { countRemoved++ delete(swagger.Components.Links, key) } @@ -469,7 +461,7 @@ func removeOrphanedComponents(swagger *openapi3.T, refs []string) int { for key := range swagger.Components.Callbacks { ref := fmt.Sprintf("#/components/callbacks/%s", key) - if !stringInSlice(ref, refs) { + if !slices.Contains(refs, ref) { countRemoved++ delete(swagger.Components.Callbacks, key) } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/resolve_names.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/resolve_names.go new file mode 100644 index 000000000..1695d67f3 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/resolve_names.go @@ -0,0 +1,339 @@ +package codegen + +import ( + "fmt" + "sort" + "strconv" + "strings" +) + +// ResolvedName holds the final Go type name assigned to a gathered schema. +type ResolvedName struct { + Schema *GatheredSchema + GoName string // The resolved Go type name + Candidate string // The initial candidate name before collision resolution + Pinned bool // True if name came from x-go-name; must not be renamed +} + +// ResolveNames takes the gathered schemas and assigns unique Go type names to each. +// It returns a map from the schema's path string to the resolved Go type name. +func ResolveNames(schemas []*GatheredSchema) map[string]string { + // Step 1: Generate candidate names for all schemas + candidates := make([]*ResolvedName, len(schemas)) + for i, s := range schemas { + candidate := generateCandidateName(s) + candidates[i] = &ResolvedName{ + Schema: s, + GoName: candidate, + Candidate: candidate, + Pinned: s.GoNameOverride != "", + } + } + + // Step 2: Resolve collisions iteratively + resolveCollisions(candidates) + + // Step 3: Build the result map + result := make(map[string]string, len(candidates)) + for _, c := range candidates { + result[c.Schema.Path.String()] = c.GoName + } + return result +} + +// generateCandidateName produces an initial Go type name candidate based on +// the schema's location and context in the OpenAPI document. +func generateCandidateName(s *GatheredSchema) string { + if s.GoNameOverride != "" { + return s.GoNameOverride + } + + switch s.Context { + case ContextComponentSchema: + return SchemaNameToTypeName(s.ComponentName) + + case ContextComponentParameter: + return SchemaNameToTypeName(s.ComponentName) + + case ContextComponentResponse: + return SchemaNameToTypeName(s.ComponentName) + + case ContextComponentRequestBody: + return SchemaNameToTypeName(s.ComponentName) + + case ContextComponentHeader: + return SchemaNameToTypeName(s.ComponentName) + + case ContextClientResponseWrapper: + // Client response wrappers use: OperationId + responseTypeSuffix + return fmt.Sprintf("%s%s", SchemaNameToTypeName(s.OperationID), responseTypeSuffix) + + case ContextOperationParameter: + if s.OperationID != "" { + return SchemaNameToTypeName(s.OperationID) + "Parameter" + } + return SchemaNameToTypeName(s.ComponentName) + "Parameter" + + case ContextOperationRequestBody: + if s.OperationID != "" { + ct := contentTypeSuffix(s.ContentType) + return SchemaNameToTypeName(s.OperationID) + ct + "Request" + } + return SchemaNameToTypeName(s.ComponentName) + "Request" + + case ContextOperationResponse: + if s.OperationID != "" { + ct := contentTypeSuffix(s.ContentType) + return SchemaNameToTypeName(s.OperationID) + s.StatusCode + ct + "Response" + } + return SchemaNameToTypeName(s.ComponentName) + "Response" + + default: + return SchemaNameToTypeName(s.ComponentName) + } +} + +// resolveCollisions detects and resolves naming collisions among the resolved names. +// It applies strategies in global phases of increasing aggressiveness: +// 1. Context suffix (Schema, Parameter, Response, etc.) +// 2. Per-schema disambiguation (content type, status code, etc.) +// 3. Numeric fallback +// +// Each strategy is applied to ALL colliding groups, then collisions are +// re-checked globally before moving to the next strategy. This prevents +// oscillation between strategies (e.g., context suffix and content type +// suffix repeatedly appending to the same names without resolution). +func resolveCollisions(names []*ResolvedName) { + strategies := []func([]*ResolvedName) bool{ + strategyContextSuffix, + strategyPerSchemaDisambiguate, + strategyNumericFallback, + } + + const maxIterations = 20 + + for _, strategy := range strategies { + for range maxIterations { + groups := groupByName(names) + anyCollision := false + anyProgress := false + for _, group := range groups { + if len(group) <= 1 { + continue + } + anyCollision = true + if strategy(group) { + anyProgress = true + } + } + if !anyCollision { + return + } + if !anyProgress { + break // This strategy can't help; try the next one + } + } + } +} + +// groupByName groups ResolvedNames by their current GoName. +func groupByName(names []*ResolvedName) map[string][]*ResolvedName { + groups := make(map[string][]*ResolvedName) + for _, n := range names { + groups[n.GoName] = append(groups[n.GoName], n) + } + return groups +} + +// strategyContextSuffix attempts to resolve collisions by appending a suffix +// derived from the schema's context (Schema, Parameter, Response, etc.). +// Component schemas are "privileged" — if exactly one member is a component +// schema, it keeps the bare name and only the others get suffixed. +// Returns true if any name was modified, false if no progress was made. +func strategyContextSuffix(group []*ResolvedName) bool { + // Count how many are component schemas (privileged) + var componentSchemaCount int + for _, n := range group { + if n.Schema.IsComponentSchema() { + componentSchemaCount++ + } + } + + progress := false + for _, n := range group { + if n.Pinned { + continue + } + + suffix := n.Schema.Context.Suffix() + if suffix == "" { + continue + } + + // If exactly one is a component schema, it keeps the bare name + if componentSchemaCount == 1 && n.Schema.IsComponentSchema() { + continue + } + + // Don't add suffix if name already ends with it + if strings.HasSuffix(n.GoName, suffix) { + continue + } + + n.GoName = n.GoName + suffix + progress = true + } + return progress +} + +// strategyPerSchemaDisambiguate tries several per-schema disambiguation strategies. +// Returns true if any name was modified, false if no progress was made. +func strategyPerSchemaDisambiguate(group []*ResolvedName) bool { + progress := tryContentTypeSuffix(group) + if !progress && tryStatusCodeSuffix(group) { + progress = true + } + if !progress && tryParamIndexSuffix(group) { + progress = true + } + return progress +} + +// tryContentTypeSuffix appends a content type discriminator when schemas +// differ by media type (e.g., JSON vs XML). +// Returns true if any name was modified, false if no progress was made. +func tryContentTypeSuffix(group []*ResolvedName) bool { + // Check if any members have different content types + contentTypes := make(map[string]bool) + for _, n := range group { + if n.Schema.ContentType != "" { + contentTypes[n.Schema.ContentType] = true + } + } + if len(contentTypes) <= 1 { + return false + } + + progress := false + for _, n := range group { + if n.Pinned { + continue + } + if n.Schema.ContentType == "" { + continue + } + suffix := contentTypeSuffix(n.Schema.ContentType) + if suffix != "" && !strings.HasSuffix(n.GoName, suffix) { + n.GoName = n.GoName + suffix + progress = true + } + } + return progress +} + +// tryStatusCodeSuffix appends the HTTP status code when schemas differ by status. +// Returns true if any name was modified, false if no progress was made. +func tryStatusCodeSuffix(group []*ResolvedName) bool { + statusCodes := make(map[string]bool) + for _, n := range group { + if n.Schema.StatusCode != "" { + statusCodes[n.Schema.StatusCode] = true + } + } + if len(statusCodes) <= 1 { + return false + } + + progress := false + for _, n := range group { + if n.Pinned { + continue + } + if n.Schema.StatusCode != "" && !strings.HasSuffix(n.GoName, n.Schema.StatusCode) { + n.GoName = n.GoName + n.Schema.StatusCode + progress = true + } + } + return progress +} + +// tryParamIndexSuffix appends a parameter index when schemas differ by position. +// Returns true if any name was modified, false if no progress was made. +func tryParamIndexSuffix(group []*ResolvedName) bool { + hasMultipleParams := false + for i := range group { + for j := i + 1; j < len(group); j++ { + if group[i].Schema.ParamIndex != group[j].Schema.ParamIndex { + hasMultipleParams = true + break + } + } + if hasMultipleParams { + break + } + } + if !hasMultipleParams { + return false + } + + progress := false + for _, n := range group { + if n.Pinned { + continue + } + suffix := strconv.Itoa(n.Schema.ParamIndex) + if !strings.HasSuffix(n.GoName, suffix) { + n.GoName = n.GoName + suffix + progress = true + } + } + return progress +} + +// strategyNumericFallback is the last resort: append increasing numbers. +// Returns true if any name was modified (always true when group has 2+ members). +func strategyNumericFallback(group []*ResolvedName) bool { + // Sort for determinism: pinned first, then component schemas, then by path + sort.Slice(group, func(i, j int) bool { + if group[i].Pinned != group[j].Pinned { + return group[i].Pinned + } + if group[i].Schema.IsComponentSchema() != group[j].Schema.IsComponentSchema() { + return group[i].Schema.IsComponentSchema() + } + return group[i].Schema.Path.String() < group[j].Schema.Path.String() + }) + + // First non-pinned keeps name, rest get numeric suffix + for i := 1; i < len(group); i++ { + if group[i].Pinned { + continue + } + group[i].GoName = group[i].GoName + strconv.Itoa(i+1) + } + return len(group) > 1 +} + +// contentTypeSuffix returns a short suffix for a media type. +func contentTypeSuffix(ct string) string { + if ct == "" { + return "" + } + ct = strings.ToLower(ct) + switch { + case strings.Contains(ct, "json"): + return "JSON" + case strings.Contains(ct, "xml"): + return "XML" + case strings.Contains(ct, "form"): + return "Form" + case strings.Contains(ct, "text"): + return "Text" + case strings.Contains(ct, "octet"): + return "Binary" + case strings.Contains(ct, "yaml"): + return "YAML" + default: + return mediaTypeToCamelCase(ct) + } +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/schema.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/schema.go index 4f983d121..e5959d2d3 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/schema.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/schema.go @@ -1,8 +1,11 @@ package codegen import ( + "encoding/json" "errors" "fmt" + "maps" + "slices" "strings" "github.com/getkin/kin-openapi/openapi3" @@ -41,6 +44,17 @@ type Schema struct { OAPISchema *openapi3.Schema } +// IsPrimitive returns true if the schema represents a primitive OpenAPI type +// (string, integer, number, or boolean) as opposed to an object, array, or +// composed type. +func (s Schema) IsPrimitive() bool { + if s.OAPISchema == nil { + return false + } + t := schemaPrimaryType(s.OAPISchema.Type) + return t.Is("string") || t.Is("integer") || t.Is("number") || t.Is("boolean") +} + func (s Schema) IsRef() bool { return s.RefType != "" } @@ -52,6 +66,32 @@ func (s Schema) IsExternalRef() bool { return strings.Contains(s.RefType, ".") } +// HasCustomMarshalJSON reports whether using this schema position as an +// underlying type of a named defined type (e.g. `type X Event` where Event is +// a oneOf union defined in components.schemas) would lose a custom +// MarshalJSON/UnmarshalJSON that we need to delegate to. This is true when +// the schema is a $ref to a oneOf/anyOf union defined elsewhere. +// +// For *local* inline unions it is deliberately false: those are generated by +// emitting the union struct at this schema position, with its own +// MarshalJSON. The template's existing $hasUnionElements branch handles +// encoding by writing .union directly, so no delegation is needed. +// +// For *external* inline unions (the response-root hoist set RefType to a +// type living in an imported package), the strict envelope is rendered as a +// defined type — `type X externalRef0.Y` — and methods on Y don't transfer. +// The .union shortcut also can't reach across packages. So we still need the +// MarshalJSON delegator here, even though UnionElements is non-empty. +func (s Schema) HasCustomMarshalJSON() bool { + if s.OAPISchema == nil { + return false + } + if len(s.UnionElements) > 0 { + return s.IsExternalRef() + } + return len(s.OAPISchema.OneOf) > 0 || len(s.OAPISchema.AnyOf) > 0 +} + func (s Schema) TypeDecl() string { if s.IsRef() { return s.RefType @@ -88,7 +128,7 @@ type Property struct { ReadOnly bool WriteOnly bool NeedsFormTag bool - Extensions map[string]interface{} + Extensions map[string]any Deprecated bool } @@ -128,10 +168,40 @@ func (p Property) GoTypeDef() string { return typeDef } +// RequiresNilCheck indicates whether the generated property should have a nil check performed on it before other checks. +// This should be used in templates when performing `nil` checks, but NOT when i.e. determining if there should be an optional pointer given to the type - in that case, use `HasOptionalPointer` +func (p Property) RequiresNilCheck() bool { + return p.ZeroValueIsNil() || p.HasOptionalPointer() +} + // HasOptionalPointer indicates whether the generated property has an optional pointer associated with it. // This takes into account the `x-go-type-skip-optional-pointer` extension, allowing a parameter definition to control whether the pointer should be skipped. func (p Property) HasOptionalPointer() bool { - return p.Required == false && p.Schema.SkipOptionalPointer == false //nolint:staticcheck + return !p.Required && !p.Schema.SkipOptionalPointer +} + +// IsPointer reports whether the generated Go field for this property is +// pointer-typed, i.e. GoTypeDef renders with a leading `*`. Templates use it +// when assigning a value to the field requires taking an address first. +func (p Property) IsPointer() bool { + return strings.HasPrefix(p.GoTypeDef(), "*") +} + +// ZeroValueIsNil is a helper function to determine if the given Go type used +// for this property has `nil` as its Go zero value. Slices (OpenAPI `array`) +// and maps (OpenAPI `object` with only `additionalProperties`, rendered as +// `map[K]V`) both satisfy this — the custom-marshal templates use it to decide +// whether to emit a nil-check before reading the field. +func (p Property) ZeroValueIsNil() bool { + if p.Schema.OAPISchema == nil { + return false + } + + if schemaPrimaryType(p.Schema.OAPISchema.Type).Is("array") { + return true + } + + return strings.HasPrefix(p.Schema.GoType, "map[") } // EnumDefinition holds type information for enum @@ -166,11 +236,33 @@ func (e *EnumDefinition) GetValues() map[string]string { return newValues } +// SecuritySchemeProvider describes one security scheme from +// components/securitySchemes for which a scopes context-key constant is +// generated. +type SecuritySchemeProvider struct { + // Name is the sanitized scheme name; templates derive the constant name + // (Scopes), the context key type name and the key's string value + // from it. + Name string + // ImportedScopes, when non-empty, is the scopes constant declared by the + // package that import-mapping assigns this scheme's $ref to (e.g. + // "externalRef0.BearerAuthScopes", or unqualified for the current + // package). The local constant is declared as an alias of it, so the + // context key — which context.Value compares by type and value — is + // shared across the generated packages. Empty means the scheme is + // declared locally with its own context key type. + ImportedScopes string +} + type Constants struct { - // SecuritySchemeProviderNames holds all provider names for security schemes. - SecuritySchemeProviderNames []string + // SecuritySchemeProviders holds all security schemes for which scopes + // context-key constants are generated. + SecuritySchemeProviders []SecuritySchemeProvider // EnumDefinitions holds type and value information for all enums EnumDefinitions []EnumDefinition + // SkipEnumValidate suppresses generation of the `Valid()` method on + // enum types. Mirrors OutputOptions.SkipEnumValidate. + SkipEnumValidate bool } // TypeDefinition describes a Go type definition in generated code. @@ -194,6 +286,67 @@ type TypeDefinition struct { // This is the Schema wrapper is used to populate the type description Schema Schema + + // ForceEnumPrefix, when true, forces the enum-value constants generated + // from this TypeDefinition to be prefixed with the type name, regardless + // of whether GenerateEnums detects a cross-enum conflict. Used for + // synthesised enum types (e.g. server-URL variable enums) whose + // generated identifiers must remain stable to avoid colliding with + // other constants emitted alongside them. + ForceEnumPrefix bool + + // Deprecated marks this type as deprecated, independent of the underlying + // schema's own deprecated flag. Used when the type is generated for a + // deprecated operation (e.g. request/response body aliases). + Deprecated bool + + // DeprecationReason is the human-readable reason for deprecation, used + // when Deprecated is true. If empty, a generic message is emitted. + DeprecationReason string + + // Comment, when set, is the full doc comment (including the leading "//") + // emitted above the type in the operation parameter template, replacing the + // default " defines parameters for ." line. Used to explain + // non-obvious generated names, e.g. the per-path hash prefix on a shared + // parameter disambiguated across paths (issue #2090). + Comment string +} + +// DeprecationComment returns a Go-style deprecation comment if the type is +// deprecated, otherwise returns an empty string. +func (t TypeDefinition) DeprecationComment() string { + if t.Deprecated { + reason := t.DeprecationReason + if reason == "" { + reason = "this type has been marked as deprecated upstream, but no `x-deprecated-reason` was set" + } + return DeprecationComment(reason) + } + if t.Schema.OAPISchema == nil || !t.Schema.OAPISchema.Deprecated { + return "" + } + reason := "this type has been marked as deprecated upstream, but no `x-deprecated-reason` was set" + if extension, ok := t.Schema.OAPISchema.Extensions[extDeprecationReason]; ok { + if r, err := extParseDeprecationReason(extension); err == nil { + reason = r + } + } + return DeprecationComment(reason) +} + +// DocComment returns the full Go doc comment for the type, including the +// deprecation notice as a separate paragraph when the type is deprecated. +func (t TypeDefinition) DocComment() string { + var comment string + if t.Schema.Description != "" { + comment = StringWithTypeNameToGoComment(t.Schema.Description, t.TypeName) + } else { + comment = fmt.Sprintf("// %s defines model for %s.", t.TypeName, t.JsonName) + } + if dc := t.DeprecationComment(); dc != "" { + comment += "\n//\n" + dc + } + return comment } // ResponseTypeDefinition is an extension of TypeDefinition, specifically for @@ -229,6 +382,97 @@ func (d *Discriminator) PropertyName() string { return SchemaNameToTypeName(d.Property) } +// DiscriminatorStamp describes how the generated From*/Merge* union helpers +// record the discriminator value for one union element. +// +// The value is always merged into the marshaled JSON via JSONPatch, so +// Discriminator() and ValueByDiscriminator() — which read the union data — +// see it immediately after From*/Merge*. Stamping at the JSON level stays +// correct regardless of how the variant declares the property — pointer, +// named enum type, renamed or absent field, or a type in an imported +// package — none of which is knowable from the union's side (see issue +// #2297). When the union struct itself declares the discriminator property, +// the helpers additionally assign Value to that field: its JSON rendering +// overwrites the union data in MarshalJSON, so the field is load-bearing. +type DiscriminatorStamp struct { + // Value is the discriminator value mapped to this union element. + Value string + // Property is the union struct's own discriminator field, matched by + // JSON property name, when it declares one; nil when it doesn't. + Property *Property + // JSONPatch is the JSON object literal merged into the union data, + // e.g. {"code":"resource_exists"}. Safe to embed in a backtick string + // literal: spec validation rejects discriminator property names and + // mapping keys containing quotes, backticks or control characters. + JSONPatch string +} + +// DiscriminatorStampFor resolves the discriminator stamp for the given union +// element, or nil when nothing should be stamped: there is no discriminator, +// the mapping doesn't cover every element, or several mapping values share +// one element type, making the value ambiguous (the 1:1 gate keeps parity +// with issue #2071). +func (s Schema) DiscriminatorStampFor(element UnionElement) *DiscriminatorStamp { + d := s.Discriminator + if d == nil || len(d.Mapping) != len(s.UnionElements) { + return nil + } + stamp := DiscriminatorStamp{} + found := false + for _, value := range SortedMapKeys(d.Mapping) { + if d.Mapping[value] == element.String() { + stamp.Value = value + found = true + } + } + if !found { + return nil + } + stamp.JSONPatch = fmt.Sprintf(`{"%s":"%s"}`, d.Property, stamp.Value) + // Match by JSON property name: the discriminator is a JSON-level + // concept, and the Go field may be renamed via x-go-name. + for i := range s.Properties { + if s.Properties[i].JsonFieldName == d.Property { + stamp.Property = &s.Properties[i] + } + } + return &stamp +} + +// DiscriminatorCase is one arm of a union's ValueByDiscriminator() switch: a +// discriminator value and the As* helper it dispatches to. +type DiscriminatorCase struct { + // Value is the discriminator value that selects this element, e.g. "GitDiffFile". + Value string + // Method is the union element method suffix, e.g. "ExternalRef0GitDiffFile", + // so the generated call is t.As(). + Method string +} + +// DiscriminatorCases returns the ValueByDiscriminator() switch arms for the +// union, sorted by discriminator value for deterministic output. Each mapping +// value is the Go type of a union element (generateUnion writes both from the +// same elementSchema.GoType), so it resolves to that element's Method(). A +// mapping value not found among the union elements is skipped: it has no As* +// helper to dispatch to, so no case is emitted and it falls through to the +// switch default. +func (s Schema) DiscriminatorCases() []DiscriminatorCase { + if s.Discriminator == nil { + return nil + } + known := make(map[string]UnionElement, len(s.UnionElements)) + for _, el := range s.UnionElements { + known[el.String()] = el + } + cases := make([]DiscriminatorCase, 0, len(s.Discriminator.Mapping)) + for _, value := range SortedMapKeys(s.Discriminator.Mapping) { + if el, ok := known[s.Discriminator.Mapping[value]]; ok { + cases = append(cases, DiscriminatorCase{Value: value, Method: el.Method()}) + } + } + return cases +} + // UnionElement describe union element, based on prefix externalRef\d+ and real ref name from external schema. type UnionElement string @@ -239,17 +483,263 @@ func (u UnionElement) String() string { // Method generate union method name for template functions `As/From/Merge`. func (u UnionElement) Method() string { - var method string - for _, part := range strings.Split(string(u), `.`) { - method += UppercaseFirstCharacter(part) + var method strings.Builder + for part := range strings.SplitSeq(string(u), `.`) { + method.WriteString(UppercaseFirstCharacter(part)) } - return method + return method.String() } func PropertiesEqual(a, b Property) bool { return a.JsonFieldName == b.JsonFieldName && a.Schema.TypeDecl() == b.Schema.TypeDecl() && a.Required == b.Required } +// schemaIsNullable reports whether an OpenAPI schema represents a nullable +// type. In OpenAPI 3.0 nullability is the explicit `nullable: true` flag; +// in OpenAPI 3.1 the `nullable` keyword was removed in favor of including +// "null" in the type array (e.g. `type: ["string","null"]`). +// +// Precondition: globalState.is31 must be set (Generate() does this at +// entry from swagger.IsOpenAPI31OrLater()). This helper does not take +// the version flag explicitly because every call site is reached +// through Generate(); calling it before globalState is initialized +// will silently take the wrong branch. +// +// Cross-version misuse (a 3.1 spec using `nullable: true`, or a 3.0 spec +// using `type: [..., "null"]`) is documented as invalid input -- this +// helper trusts its input to use the version-appropriate idiom. +func schemaIsNullable(s *openapi3.Schema) bool { + return schemaIsNullableRec(s, nil) +} + +// schemaIsNullableRec is schemaIsNullable's implementation, carrying a +// `seen` set of already-visited schema values so that a cyclic allOf (a +// $ref member resolving back to an ancestor — the same cycles +// mergeOpenapiSchemas guards against) cannot cause unbounded recursion. +// The set is allocated lazily: the common case (no allOf) never touches it. +func schemaIsNullableRec(s *openapi3.Schema, seen map[*openapi3.Schema]bool) bool { + if s == nil { + return false + } + // A nullable member inside an allOf makes the whole schema nullable. + // In OpenAPI 3.0, allOf is the only place a sibling (`nullable: true`) + // may sit next to a $ref, so wrapping a $ref in allOf is the idiomatic + // way to decorate a referenced schema as nullable (issue #1898). This + // is where the flag lives — the outer schema's own Nullable is unset — + // so descend into the members. Descent is transitive to match the + // transitive allOf flattening in mergeOpenapiSchemas, and equally valid + // under 3.1's type-array idiom, hence checked before the version + // branch. mergeOpenapiSchemas unions nullability into the merged type; + // this surfaces it at the use site so the field is wrapped in a pointer + // / nullable.Nullable[T]. + for _, member := range s.AllOf { + if member == nil || member.Value == nil || seen[member.Value] { + continue + } + if seen == nil { + seen = make(map[*openapi3.Schema]bool) + } + seen[member.Value] = true + if schemaIsNullableRec(member.Value, seen) { + return true + } + } + if globalState.is31 { + if s.Type != nil && s.Type.Includes("null") { + return true + } + // OpenAPI 3.1 also allows nullability to be expressed via an + // `anyOf` or `oneOf` branch whose only type is "null", which is + // semantically equivalent to including "null" in the outer + // type array. Detect it here so downstream code that wraps in a + // pointer (or nullable.Nullable[T]) reaches the right decision. + for _, branch := range s.AnyOf { + if branch != nil && isNullTypeSchema(branch.Value) { + return true + } + } + for _, branch := range s.OneOf { + if branch != nil && isNullTypeSchema(branch.Value) { + return true + } + } + return false + } + return s.Nullable +} + +// isNullTypeSchema reports whether an OpenAPI 3.1 schema is a bare +// `{"type": "null"}` -- i.e. a schema whose only type is "null" and +// which is otherwise empty of constraints. Used to detect the +// nullability-via-anyOf idiom in `schemaIsNullable` and to filter such +// branches out of `generateUnion` (they're nullability markers, not +// union variants for which we need a Go type). +func isNullTypeSchema(s *openapi3.Schema) bool { + if s == nil || s.Type == nil { + return false + } + slice := s.Type.Slice() + return len(slice) == 1 && slice[0] == "null" +} + +// enumViaOneOfValue is one branch of an OpenAPI 3.1 enum-via-oneOf schema. +// Title is the per-branch identifier (becomes the Go constant name); Value +// is the stringified `const` (the Go literal, unquoted; the enum +// generation pipeline applies the appropriate quoting via ValueWrapper). +type enumViaOneOfValue struct { + Title string + Value string +} + +// detectEnumViaOneOf reports whether the schema matches the OpenAPI 3.1 +// enum-via-oneOf idiom and, if so, returns the per-branch values in +// declaration order. +// +// The idiom: +// +// Severity: +// type: integer # or "string" +// oneOf: +// - title: HIGH +// const: 2 +// description: An urgent problem # optional +// - ... +// +// All members must carry both `title` and `const`; no member may itself +// be a composition (oneOf/allOf/anyOf) or declare properties. The outer +// schema's primary type must be a scalar (string or integer). +// +// Gated on globalState.is31 (the keyword `const` lands in OpenAPI 3.1) +// AND !SkipEnumViaOneOf so users can fall through to the standard union +// generator on demand. +// +// Precondition: globalState (both is31 and options) must be initialized +// (see schemaIsNullable for context). +func detectEnumViaOneOf(schema *openapi3.Schema) ([]enumViaOneOfValue, bool) { + if !globalState.is31 { + return nil, false + } + if globalState.options.OutputOptions.SkipEnumViaOneOf { + return nil, false + } + if schema == nil || len(schema.OneOf) == 0 { + return nil, false + } + primary := schemaPrimaryType(schema.Type) + if primary == nil { + return nil, false + } + if !primary.Is("string") && !primary.Is("integer") { + return nil, false + } + items := make([]enumViaOneOfValue, 0, len(schema.OneOf)) + for _, ref := range schema.OneOf { + if ref == nil || ref.Value == nil { + return nil, false + } + m := ref.Value + if m.Title == "" || m.Const == nil { + return nil, false + } + if len(m.OneOf) > 0 || len(m.AllOf) > 0 || len(m.AnyOf) > 0 { + return nil, false + } + if len(m.Properties) > 0 { + return nil, false + } + items = append(items, enumViaOneOfValue{ + Title: m.Title, + Value: fmt.Sprintf("%v", m.Const), + }) + } + return items, true +} + +// describeWithExamples folds a schema's example data into its +// description string for use in generated Go doc comments. Version-aware: +// in 3.0 it reads schema.Example (singular); in 3.1 it reads +// schema.Examples (plural array). Cross-version misuse is documented as +// invalid input -- this helper does not look at the off-version field. +// +// The output appends `Examples: , , ...` (or `Example: ` in +// 3.0) on a new paragraph after any existing description text. Non- +// string values are JSON-encoded so structured examples render +// readably. +// +// Precondition: globalState.is31 must be set (see schemaIsNullable +// for context). +func describeWithExamples(description string, schema *openapi3.Schema) string { + if schema == nil { + return description + } + var values []any + label := "Example" + if globalState.is31 { + if len(schema.Examples) == 0 { + return description + } + values = schema.Examples + label = "Examples" + } else { + if schema.Example == nil { + return description + } + values = []any{schema.Example} + } + + formatted := make([]string, 0, len(values)) + for _, v := range values { + formatted = append(formatted, formatExampleValue(v)) + } + + suffix := label + ": " + strings.Join(formatted, ", ") + if description == "" { + return suffix + } + return description + "\n\n" + suffix +} + +// formatExampleValue renders an example value for inclusion in a Go doc +// comment. Strings round-trip as themselves (no JSON quoting); other +// values JSON-encode so structured examples remain readable. +func formatExampleValue(v any) string { + if s, ok := v.(string); ok { + return s + } + if b, err := json.Marshal(v); err == nil { + return string(b) + } + return fmt.Sprintf("%v", v) +} + +// schemaPrimaryType returns the type slice used for primitive-type dispatch +// (`*Types.Is("string")` etc.). In OpenAPI 3.0 the type slice has at most +// one entry and is returned unchanged. In OpenAPI 3.1 the "null" entry is +// the nullability indicator (handled separately by schemaIsNullable); we +// strip it here so the rest of the codegen can keep dispatching with +// `Is("...")` as it always has. +// +// Precondition: globalState.is31 must be set (see schemaIsNullable +// for context). The early-return on `!globalState.is31` is correct only +// when the global state has actually been initialized -- a call before +// Generate() runs would silently use the 3.0 branch. +func schemaPrimaryType(t *openapi3.Types) *openapi3.Types { + if t == nil || !globalState.is31 { + return t + } + s := t.Slice() + if len(s) <= 1 { + return t + } + stripped := make(openapi3.Types, 0, len(s)) + for _, name := range s { + if name != "null" { + stripped = append(stripped, name) + } + } + return &stripped +} + func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { // Add a fallback value in case the sref is nil. // i.e. the parent schema defines a type:array, but the array has @@ -259,30 +749,70 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { } schema := sref.Value + extensions := combinedSchemaExtensions(sref) + + // Check x-go-type-skip-optional-pointer, which will override if the type + // should be a pointer or not when the field is optional. + // NOTE skipOptionalPointer will be defaulted to the global value, but can be overridden on a per-type/-field basis + skipOptionalPointer := globalState.options.OutputOptions.PreferSkipOptionalPointer + if extension, ok := extensions[extPropGoTypeSkipOptionalPointer]; ok { + var err error + skipOptionalPointer, err = extParsePropGoTypeSkipOptionalPointer(extension) + if err != nil { + return Schema{}, fmt.Errorf("invalid value for %q: %w", extPropGoTypeSkipOptionalPointer, err) + } + } // If Ref is set on the SchemaRef, it means that this type is actually a reference to // another type. We're not de-referencing, so simply use the referenced type. if IsGoTypeReference(sref.Ref) { - // Convert the reference path to Go type - refType, err := RefPathToGoType(sref.Ref) - if err != nil { - return Schema{}, fmt.Errorf("error turning reference (%s) into a Go type: %s", - sref.Ref, err) + var refType string + + // check if there is an x-go-type extension next to the $ref and use that over the overrided type. + if extension, ok := sref.Extensions[extPropGoType]; ok { + var ok bool + refType, ok = extension.(string) + if !ok { + return Schema{}, fmt.Errorf("error turning '%s: %v' into string", + extPropGoType, extension) + } + } else { + // Convert the reference path to Go type + var err error + refType, err = RefPathToGoType(sref.Ref) + if err != nil { + return Schema{}, fmt.Errorf("error turning reference (%s) into a Go type: %s", + sref.Ref, err) + } } + return Schema{ GoType: refType, - Description: schema.Description, + Description: describeWithExamples(schema.Description, schema), DefineViaAlias: true, + SkipOptionalPointer: skipOptionalPointer, OAPISchema: schema, - SkipOptionalPointer: globalState.options.OutputOptions.PreferSkipOptionalPointer, }, nil } outSchema := Schema{ - Description: schema.Description, - OAPISchema: schema, - // NOTE that SkipOptionalPointer will be defaulted to the global value, but can be overridden on a per-type/-field basis - SkipOptionalPointer: globalState.options.OutputOptions.PreferSkipOptionalPointer, + Description: describeWithExamples(schema.Description, schema), + OAPISchema: schema, + SkipOptionalPointer: skipOptionalPointer, + } + + // Check x-go-type, which will completely override the definition of this + // schema with the provided type. This must be checked before AllOf so + // that an override on the outer schema wins over allOf composition. + if extension, ok := extensions[extPropGoType]; ok { + typeName, err := extTypeName(extension) + if err != nil { + return outSchema, fmt.Errorf("invalid value for %q: %w", extPropGoType, err) + } + outSchema.GoType = typeName + outSchema.DefineViaAlias = true + + return outSchema, nil } // AllOf is interesting, and useful. It's the union of a number of other @@ -290,39 +820,106 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { // so that in a RESTful paradigm, the Create operation can return // (object, id), so that other operations can refer to (id) if schema.AllOf != nil { - mergedSchema, err := MergeSchemas(schema.AllOf, path) + var mergedSchema Schema + var err error + // Behavior is gated on Compatibility.OldAllOfSiblingMerging: + // when set, the parent's structural siblings and Description are + // silently discarded (the historical behavior). When unset + // (default), they are merged into the result. + mergeSiblings := !globalState.options.Compatibility.OldAllOfSiblingMerging + if mergeSiblings && hasStructuralSiblings(schema) { + // Inject the parent (with AllOf cleared) as the final allOf + // member so its structural siblings — Properties, Required, + // AdditionalProperties — are merged with the allOf members + // rather than discarded. Issues #697, #931, #1710, #2102. + // + // Allocate a fresh slice rather than appending to schema.AllOf + // directly: if kin-openapi gave us a slice with spare capacity, + // `append` would write the new element into the shared backing + // array, mutating any other view that has been extended past + // len(schema.AllOf). + s := *schema + s.AllOf = nil + allOfRefs := make([]*openapi3.SchemaRef, 0, len(schema.AllOf)+1) + allOfRefs = append(allOfRefs, schema.AllOf...) + allOfRefs = append(allOfRefs, &openapi3.SchemaRef{Value: &s}) + mergedSchema, err = MergeSchemas(allOfRefs, path) + } else { + // Either the user opted into legacy behavior, or the parent is + // a pure wrapper with no structural siblings. In the wrapper + // case, MergeSchemas' single-element fast path returns the + // referenced type unchanged, preserving named-type identity. + mergedSchema, err = MergeSchemas(schema.AllOf, path) + } if err != nil { return Schema{}, fmt.Errorf("error merging schemas: %w", err) } mergedSchema.OAPISchema = schema + // Description is metadata, not a structural constraint, so it + // doesn't go through the merge. Copy it from the parent when set. + // Issue #1960. Gated on the same compatibility flag as the + // sibling-merge above. + if mergeSiblings && schema.Description != "" { + mergedSchema.Description = schema.Description + } + // x-go-type on the parent is handled by the early return above + // (combined extensions). For x-go-type-skip-optional-pointer, only + // override the merged value when the parent sets it explicitly — + // otherwise we would clobber the value MergeSchemas computed from + // the decorator idiom (an inline allOf member that carries the + // extension; see merge_schemas.go and issue #1957). + if _, ok := extensions[extPropGoTypeSkipOptionalPointer]; ok { + mergedSchema.SkipOptionalPointer = skipOptionalPointer + } return mergedSchema, nil } - // Check x-go-type-skip-optional-pointer, which will override if the type - // should be a pointer or not when the field is optional. - if extension, ok := schema.Extensions[extPropGoTypeSkipOptionalPointer]; ok { - skipOptionalPointer, err := extParsePropGoTypeSkipOptionalPointer(extension) - if err != nil { - return outSchema, fmt.Errorf("invalid value for %q: %w", extPropGoTypeSkipOptionalPointer, err) + // OpenAPI 3.1 enum-via-oneOf: a scalar schema whose oneOf branches + // each carry `title` + `const` is rendered as a Go typed enum, not as + // a union. Detection is gated by version + the SkipEnumViaOneOf flag; + // when the idiom does not match, fall through to standard handling + // (which routes oneOf into generateUnion further below). + if items, ok := detectEnumViaOneOf(schema); ok { + if err := oapiSchemaToGoType(schema, path, &outSchema); err != nil { + return Schema{}, fmt.Errorf("error resolving primitive type for enum-via-oneOf: %w", err) } - outSchema.SkipOptionalPointer = skipOptionalPointer - } - - // Check x-go-type, which will completely override the definition of this - // schema with the provided type. - if extension, ok := schema.Extensions[extPropGoType]; ok { - typeName, err := extTypeName(extension) - if err != nil { - return outSchema, fmt.Errorf("invalid value for %q: %w", extPropGoType, err) + // Force a typed declaration -- enums must not be aliased. + outSchema.DefineViaAlias = false + outSchema.EnumValues = make(map[string]string, len(items)) + for _, it := range items { + outSchema.EnumValues[SchemaNameToTypeName(it.Title)] = it.Value + } + // Non-toplevel schemas need an explicit AdditionalType so the + // downstream EnumDefinition collector picks them up; toplevel + // schemas are already collected via the components walk. + if len(path) > 1 { + var typeName string + if extension, ok := schema.Extensions[extGoTypeName]; ok { + tn, err := extString(extension) + if err != nil { + return outSchema, fmt.Errorf("invalid value for %q: %w", extGoTypeName, err) + } + typeName = tn + } else { + typeName = SchemaNameToTypeName(PathToTypeName(path)) + } + typeDef := TypeDefinition{ + TypeName: typeName, + JsonName: strings.Join(path, "."), + Schema: outSchema, + } + outSchema.AdditionalTypes = append(outSchema.AdditionalTypes, typeDef) + outSchema.RefType = typeName } - outSchema.GoType = typeName - outSchema.DefineViaAlias = true - return outSchema, nil } - // Schema type and format, eg. string / binary - t := schema.Type + // Schema type and format, eg. string / binary. In OpenAPI 3.1, `type` + // may include "null" alongside the primary type to express nullability; + // strip "null" up front so the object / fall-through dispatch sees the + // underlying type. Nullability itself is captured by schemaIsNullable() + // at the call sites that wrap the result in a pointer. + t := schemaPrimaryType(schema.Type) // Handle objects and empty schemas first as a special case if t.Slice() == nil || t.Is("object") { var outType string @@ -410,7 +1007,7 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { return Schema{}, fmt.Errorf("error generating Go schema for property '%s': %w", pName, err) } - required := StringInArray(pName, schema.Required) + required := slices.Contains(schema.Required, pName) if (pSchema.HasAdditionalProperties || len(pSchema.UnionElements) != 0) && pSchema.RefType == "" { // If we have fields present which have additional properties or union values, @@ -430,17 +1027,18 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { } description := "" if p.Value != nil { - description = p.Value.Description + description = describeWithExamples(p.Value.Description, p.Value) } + prop := Property{ JsonFieldName: pName, Schema: pSchema, Required: required, Description: description, - Nullable: p.Value.Nullable, + Nullable: schemaIsNullable(p.Value), ReadOnly: p.Value.ReadOnly, WriteOnly: p.Value.WriteOnly, - Extensions: p.Value.Extensions, + Extensions: combinedSchemaExtensions(p), Deprecated: p.Value.Deprecated, } outSchema.Properties = append(outSchema.Properties, prop) @@ -460,13 +1058,21 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { } } - outSchema.GoType = GenStructFromSchema(outSchema) + // Only generate a struct literal if the schema actually has + // struct content. When `generateUnion` collapses a one- + // element nullable union (`anyOf: [{type: X}, {type: "null"}]`) + // down to the bare X branch, it sets outSchema.GoType to the + // primitive's Go type and clears the struct-shaped fields; + // rebuilding `struct {}` here would clobber that. + if len(outSchema.Properties) > 0 || outSchema.HasAdditionalProperties || len(outSchema.UnionElements) > 0 { + outSchema.GoType = GenStructFromSchema(outSchema) + } } // Check for x-go-type-name. It behaves much like x-go-type, however, it will // create a type definition for the named type, and use the named type in place // of this schema. - if extension, ok := schema.Extensions[extGoTypeName]; ok { + if extension, ok := extensions[extGoTypeName]; ok { typeName, err := extTypeName(extension) if err != nil { return outSchema, fmt.Errorf("invalid value for %q: %w", extGoTypeName, err) @@ -474,6 +1080,7 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { newTypeDef := TypeDefinition{ TypeName: typeName, + JsonName: strings.Join(path, "."), Schema: outSchema, } outSchema = Schema{ @@ -484,8 +1091,52 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { } } + // Auto-hoist anonymous inline schemas to a named type when the + // output-options.generate-types-for-anonymous-schemas option is set. + // Mirrors the x-go-type-name path above but with a path-derived name + // and a distinct named type (DefineViaAlias=false). Only fires for + // schemas nested inside another schema (len(path) > 1) — top-level + // component schemas already have their own names. Skipped if the + // schema is already a named ref or has just been wrapped by + // x-go-type-name (DefineViaAlias=true above). + // See https://github.com/oapi-codegen/oapi-codegen/issues/1139 + if globalState.options.OutputOptions.GenerateTypesForAnonymousSchemas && + len(path) > 1 && + outSchema.RefType == "" && + !outSchema.DefineViaAlias && + hasInlineStructuralContent(&outSchema) { + typeName := PathToTypeName(path) + typeDef := TypeDefinition{ + TypeName: typeName, + JsonName: strings.Join(path, "."), + Schema: outSchema, + } + outSchema = Schema{ + Description: typeDef.Schema.Description, + GoType: typeName, + // Mark the returned schema as a reference to the + // hoisted named type. This mirrors the convention + // followed by every other hoist path in this file + // (additionalProperties-hoist, property-hoist, + // array-item-hoist) and prevents any future hoist + // caller that asks "has this schema been named yet?" + // from re-hoisting under the same path-derived name. + RefType: typeName, + AdditionalTypes: append(outSchema.AdditionalTypes, typeDef), + } + } + return outSchema, nil - } else if len(schema.Enum) > 0 { + } else if (len(schema.Enum) > 0 || (globalState.is31 && schema.Const != nil)) && !t.Is("array") { + // An `enum` (or 3.1 `const`) is only meaningful on a scalar schema: + // it constrains a single value, which we render as a typed constant. + // A schema that combines `type: array` with an `enum` is malformed -- + // the enum members are scalars, not arrays, so no array value could + // satisfy it (seen in the wild as a copy of the item enum onto the + // array; see issue #2176). Emitting constants there yields + // `const X SliceType = ...`, which is not a valid Go constant type. + // Fall through to normal array handling so the schema generates as a + // plain `[]ItemType`; the item schema carries the real enum. err := oapiSchemaToGoType(schema, path, &outSchema) // Enums need to be typed, so that the values aren't interchangeable, // so no matter what schema conversion thinks, we need to define a @@ -495,14 +1146,22 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { if err != nil { return Schema{}, fmt.Errorf("error resolving primitive type: %w", err) } - enumValues := make([]string, len(schema.Enum)) - for i, enumValue := range schema.Enum { + // OpenAPI 3.1 `const: X` is shorthand for `enum: [X]`. Fold the + // two through the same enum-codegen path here so a top-level + // `const` schema becomes a typed alias plus a singleton constant, + // using the same name-derivation rules `enum` uses. + enumSource := schema.Enum + if len(enumSource) == 0 { + enumSource = []any{schema.Const} + } + enumValues := make([]string, len(enumSource)) + for i, enumValue := range enumSource { enumValues[i] = fmt.Sprintf("%v", enumValue) } enumNames := enumValues for _, key := range []string{extEnumVarNames, extEnumNames} { - if extension, ok := schema.Extensions[key]; ok { + if extension, ok := extensions[key]; ok { if extEnumNames, err := extParseEnumVarNames(extension); err == nil { enumNames = extEnumNames break @@ -530,7 +1189,7 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { // Allow overriding autogenerated enum type names, since these may // cause conflicts - see https://github.com/oapi-codegen/oapi-codegen/issues/832 var typeName string - if extension, ok := schema.Extensions[extGoTypeName]; ok { + if extension, ok := extensions[extGoTypeName]; ok { typeName, err = extString(extension) if err != nil { return outSchema, fmt.Errorf("invalid value for %q: %w", extGoTypeName, err) @@ -560,7 +1219,13 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { // all non-object types. func oapiSchemaToGoType(schema *openapi3.Schema, path []string, outSchema *Schema) error { f := schema.Format - t := schema.Type + // In OpenAPI 3.1, `type` may be a multi-element array including "null" + // to express nullability. The dispatch below uses `*Types.Is("...")`, + // which only matches single-element type slices. Strip "null" up front + // so the dispatch sees the underlying primitive type. Nullability + // itself was already captured by schemaIsNullable() at the call sites + // that wrap the result in a pointer. + t := schemaPrimaryType(schema.Type) if t.Is("array") { // For arrays, we'll get the type of the Items and throw a @@ -569,11 +1234,19 @@ func oapiSchemaToGoType(schema *openapi3.Schema, path []string, outSchema *Schem if err != nil { return fmt.Errorf("error generating type for array: %w", err) } - if (arrayType.HasAdditionalProperties || len(arrayType.UnionElements) != 0) && arrayType.RefType == "" { + + if (arrayType.HasAdditionalProperties || + len(arrayType.UnionElements) != 0 || + (globalState.options.OutputOptions.GenerateTypesForAnonymousSchemas && len(arrayType.Properties) > 0)) && + arrayType.RefType == "" { // If we have items which have additional properties or union values, // but are not a pre-defined type, we need to define a type // for them, which will be based on the field names we followed - // to get to the type. + // to get to the type. The third clause catches plain inline + // object items under generate-types-for-anonymous-schemas: the + // auto-hoist block in GenerateGoSchema only fires when + // len(path) > 1, so top-level array schemas (path length 1) fall + // through with their items left anonymous unless we hoist here. typeName := PathToTypeName(append(path, "Item")) typeDef := TypeDefinition{ @@ -585,75 +1258,103 @@ func oapiSchemaToGoType(schema *openapi3.Schema, path []string, outSchema *Schem arrayType.RefType = typeName } + + typeDeclaration := arrayType.TypeDecl() + if schemaIsNullable(arrayType.OAPISchema) { + if globalState.options.OutputOptions.NullableType { + typeDeclaration = "nullable.Nullable[" + typeDeclaration + "]" + } else { + typeDeclaration = "*" + typeDeclaration + } + } + outSchema.ArrayType = &arrayType - outSchema.GoType = "[]" + arrayType.TypeDecl() + outSchema.GoType = "[]" + typeDeclaration outSchema.AdditionalTypes = arrayType.AdditionalTypes outSchema.Properties = arrayType.Properties outSchema.DefineViaAlias = true - if sliceContains(globalState.options.OutputOptions.DisableTypeAliasesForType, "array") { + if slices.Contains(globalState.options.OutputOptions.DisableTypeAliasesForType, "array") { outSchema.DefineViaAlias = false } setSkipOptionalPointerForContainerType(outSchema) } else if t.Is("integer") { - // We default to int if format doesn't ask for something else. - switch f { - case "int64", - "int32", - "int16", - "int8", - "int", - "uint64", - "uint32", - "uint16", - "uint8", - "uint": - outSchema.GoType = f - default: - outSchema.GoType = "int" - } + spec := globalState.typeMapping.Integer.Resolve(f) + outSchema.GoType = spec.Type outSchema.DefineViaAlias = true } else if t.Is("number") { - // We default to float for "number" - switch f { - case "double": - outSchema.GoType = "float64" - case "float", "": - outSchema.GoType = "float32" - default: - return fmt.Errorf("invalid number format: %s", f) - } + spec := globalState.typeMapping.Number.Resolve(f) + outSchema.GoType = spec.Type outSchema.DefineViaAlias = true } else if t.Is("boolean") { - if f != "" { - return fmt.Errorf("invalid format (%s) for boolean", f) - } - outSchema.GoType = "bool" + spec := globalState.typeMapping.Boolean.Resolve(f) + outSchema.GoType = spec.Type outSchema.DefineViaAlias = true } else if t.Is("string") { - // Special case string formats here. - switch f { - case "byte": - outSchema.GoType = "[]byte" + // OpenAPI 3.1: `contentMediaType` and `contentEncoding` are the + // JSON-Schema-aligned replacements for the 3.0 `format: binary` + // / `format: byte` idioms used to describe file uploads and + // base64-encoded binary data. When the spec author writes the + // 3.1 form (per learn.openapis.org/upgrading/v3.0-to-v3.1.html + // "Update file upload descriptions"), the schema arrives with + // no `format` set but one of these keywords populated; without + // this synthesis the field would fall through to the default + // `string` mapping and the user would silently lose the + // file/binary typing they had under 3.0. We only synthesize a + // format when the user has not explicitly set one, so any + // explicit `format` (or user-provided `type-mapping` overlay) + // continues to win. + resolvedFormat := f + if globalState.is31 && resolvedFormat == "" { + switch { + case schema.ContentMediaType != "": + // Raw binary content of any media type -- equivalent to + // 3.0 `format: binary`. Routes to openapi_types.File in + // the default mapping. + resolvedFormat = "binary" + case schema.ContentEncoding == "base64": + // Standard padded base64 is the one RFC4648 variant Go's + // encoding/json handles natively for []byte. Map to + // `byte` (-> []byte), matching the 3.0 `format: byte` + // behavior so the wire encoding is honored on + // marshal/unmarshal without user code. + resolvedFormat = "byte" + // Other contentEncoding values (base64url, base32, base16, + // quoted-printable) are intentionally NOT mapped to []byte. + // Go's JSON codec for []byte always emits/expects standard + // padded base64; using it for base64url would silently + // corrupt URL-safe characters (`-`/`_`) on unmarshal and + // re-emit them as standard base64 on marshal. Leaving the + // field as the default `string` keeps the raw declared + // wire encoding in user hands -- they can apply the + // correct codec at the application layer. Users who want + // a typed mapping can override via `type-mapping` (e.g. + // map a custom format to a wrapper type that implements + // encoding-aware MarshalJSON/UnmarshalJSON). + } + } + spec := globalState.typeMapping.String.Resolve(resolvedFormat) + outSchema.GoType = spec.Type + // Preserve special behaviors for specific types + if outSchema.GoType == "[]byte" { setSkipOptionalPointerForContainerType(outSchema) - case "email": - outSchema.GoType = "openapi_types.Email" - case "date": - outSchema.GoType = "openapi_types.Date" - case "date-time": - outSchema.GoType = "time.Time" - case "json": - outSchema.GoType = "json.RawMessage" + } + if outSchema.GoType == "json.RawMessage" { outSchema.SkipOptionalPointer = true - case "uuid": - outSchema.GoType = "openapi_types.UUID" - case "binary": - outSchema.GoType = "openapi_types.File" - default: - // All unrecognized formats are simply a regular string. - outSchema.GoType = "string" } outSchema.DefineViaAlias = true + } else if t.Is("null") { + // OpenAPI 3.1 allows a schema whose only type is "null", which + // validates exactly the JSON value null. Go has no type whose only + // value is null, so map it to `any` — the same permissive mapping + // used for typeless schemas — rather than rejecting an otherwise + // valid spec. An optional pointer makes no sense for `any`, whose + // zero value is already nil, so skip it — mirroring the + // typeless-schema handling in GenerateGoSchema. + // See https://github.com/oapi-codegen/oapi-codegen/issues/2430 + outSchema.GoType = "any" + outSchema.SkipOptionalPointer = true + outSchema.DefineViaAlias = true } else { return fmt.Errorf("unhandled Schema type: %v", t) } @@ -726,11 +1427,7 @@ func GenFieldsFromProperties(props []Property) []string { shouldOmitEmpty := (!p.Required || p.ReadOnly || p.WriteOnly) && (!p.Required || !p.ReadOnly || !globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer) - omitEmpty := !p.Nullable && shouldOmitEmpty - - if p.Nullable && globalState.options.OutputOptions.NullableType { - omitEmpty = shouldOmitEmpty - } + omitEmpty := shouldOmitEmpty omitZero := false @@ -798,7 +1495,7 @@ func additionalPropertiesType(schema Schema) string { if schema.AdditionalPropertiesType.RefType != "" { addPropsType = schema.AdditionalPropertiesType.RefType } - if schema.AdditionalPropertiesType.OAPISchema != nil && schema.AdditionalPropertiesType.OAPISchema.Nullable { + if schemaIsNullable(schema.AdditionalPropertiesType.OAPISchema) { addPropsType = "*" + addPropsType } return addPropsType @@ -866,8 +1563,68 @@ func generateUnion(outSchema *Schema, elements openapi3.SchemaRefs, discriminato } } + // First pass: count effective (non-null) branches. In OpenAPI 3.1, a + // bare `{"type": "null"}` branch in anyOf/oneOf is a nullability + // marker, not a real union variant -- there's no Go type that + // corresponds to "only the JSON value null". The parent schema's + // nullability is captured by schemaIsNullable, which inspects + // anyOf/oneOf for the same idiom and wraps the result in a pointer + // at the call site. + effectiveCount := 0 + hadNullBranch := false + var soleEffective *openapi3.SchemaRef + for _, e := range elements { + if e != nil && isNullTypeSchema(e.Value) { + hadNullBranch = true + continue + } + effectiveCount++ + if soleEffective == nil { + soleEffective = e + } + } + + // Collapse: if filtering out null branches leaves exactly one + // effective branch and there is no discriminator, the schema is + // semantically equivalent to that single branch (made nullable by + // the original null branch). Produce the same Go shape the + // type-array idiom would: `anyOf: [{type: string}, {type: "null"}]` + // must generate the same `*string` field as `type: ["string", + // "null"]`. Without this, the single remaining branch would be + // wrapped in a one-variant union type, exposing a needless + // `FromX`/`AsX` accessor API. + // + // We do not collapse when there was no null branch (`anyOf: [{type: + // X}]` alone) to avoid changing behavior for existing single-branch + // union specs that may rely on the wrapper shape. The narrow + // condition keeps this change scoped to the bug fix. + if effectiveCount == 1 && hadNullBranch && discriminator == nil { + elementSchema, err := GenerateGoSchema(soleEffective, path) + if err != nil { + return err + } + // Inherit the single branch's underlying representation. The + // caller will apply nullability (schemaIsNullable returns true + // because the original anyOf/oneOf contained a null branch). + outSchema.GoType = elementSchema.GoType + outSchema.RefType = elementSchema.RefType + outSchema.DefineViaAlias = elementSchema.DefineViaAlias + outSchema.Properties = elementSchema.Properties + outSchema.HasAdditionalProperties = elementSchema.HasAdditionalProperties + outSchema.AdditionalPropertiesType = elementSchema.AdditionalPropertiesType + outSchema.ArrayType = elementSchema.ArrayType + outSchema.SkipOptionalPointer = elementSchema.SkipOptionalPointer + outSchema.AdditionalTypes = append(outSchema.AdditionalTypes, elementSchema.AdditionalTypes...) + return nil + } + refToGoTypeMap := make(map[string]string) for i, element := range elements { + // Skip null-only branches: nullability marker, not a real + // union variant. See the collapse comment above for context. + if element != nil && isNullTypeSchema(element.Value) { + continue + } elementPath := append(path, fmt.Sprint(i)) elementSchema, err := GenerateGoSchema(element, elementPath) if err != nil { @@ -879,7 +1636,7 @@ func generateUnion(outSchema *Schema, elements openapi3.SchemaRefs, discriminato if elementSchema.TypeDecl() == elementName { elementSchema.GoType = elementName } else { - td := TypeDefinition{Schema: elementSchema, TypeName: elementName} + td := TypeDefinition{Schema: elementSchema, TypeName: elementName, JsonName: strings.Join(elementPath, ".")} outSchema.AdditionalTypes = append(outSchema.AdditionalTypes, td) elementSchema.GoType = td.TypeName } @@ -896,10 +1653,9 @@ func generateUnion(outSchema *Schema, elements openapi3.SchemaRefs, discriminato // Explicit mapping. var mapped bool for k, v := range discriminator.Mapping { - if v == element.Ref { + if v.Ref == element.Ref { outSchema.Discriminator.Mapping[k] = elementSchema.GoType mapped = true - break } } // Implicit mapping. @@ -910,7 +1666,14 @@ func generateUnion(outSchema *Schema, elements openapi3.SchemaRefs, discriminato outSchema.UnionElements = append(outSchema.UnionElements, UnionElement(elementSchema.GoType)) } - if (outSchema.Discriminator != nil) && len(outSchema.Discriminator.Mapping) != len(elements) { + // Compare against effectiveCount (non-null branches actually + // processed) rather than len(elements). For a nullable + // discriminated union (`oneOf: [Cat, Dog, {type: "null"}]`), the + // null-branch skip above leaves the discriminator with one fewer + // mapping than the raw element count, and we must not flag that as + // incomplete -- the null branch is a nullability marker, not a real + // variant that needs a mapping. + if (outSchema.Discriminator != nil) && len(outSchema.Discriminator.Mapping) < effectiveCount { return errors.New("discriminator: not all schemas were mapped") } @@ -927,3 +1690,51 @@ func setSkipOptionalPointerForContainerType(outSchema *Schema) { outSchema.SkipOptionalPointer = true } + +// combinedSchemaExtensions returns one set of extensions taking +// those from next to the $ref and the referenced schema itself. +// Extensions next to the $ref take precedence. +func combinedSchemaExtensions(r *openapi3.SchemaRef) map[string]any { + combined := map[string]any{} + + if r.Value != nil { + maps.Copy(combined, r.Value.Extensions) + } + + maps.Copy(combined, r.Extensions) + + return combined +} + +// hasStructuralSiblings reports whether a schema with allOf also carries +// fields outside allOf that materially affect the generated Go type. +// Such fields must be merged with the allOf members rather than discarded. +// +// Description and Title are excluded — they are metadata, not structural, +// and the caller propagates them separately. Nullable/ReadOnly/WriteOnly +// are also excluded for now: their strict-equality check in +// mergeOpenapiSchemas conflates the bool zero value with "unset" and would +// regress simple wrappers like {allOf: [X-with-nullable:true]}. +func hasStructuralSiblings(s *openapi3.Schema) bool { + if s == nil { + return false + } + return len(s.Properties) > 0 || + len(s.Required) > 0 || + s.AdditionalProperties.Has != nil || + s.AdditionalProperties.Schema != nil +} + +// hasInlineStructuralContent reports whether a generated Schema is an +// anonymous inline that the generate-types-for-anonymous-schemas option +// should turn into a named type. Pure scalars and aliases-to-refs are +// excluded; objects with properties, schemas with additional-properties, +// and union schemas all qualify. +func hasInlineStructuralContent(s *Schema) bool { + if s == nil { + return false + } + return len(s.Properties) > 0 || + s.HasAdditionalProperties || + len(s.UnionElements) > 0 +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/server_urls.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/server_urls.go index d10c2d9f6..b70a52fd5 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/server_urls.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/server_urls.go @@ -2,7 +2,10 @@ package codegen import ( "fmt" + "regexp" + "sort" "strconv" + "strings" "text/template" "github.com/getkin/kin-openapi/openapi3" @@ -11,6 +14,28 @@ import ( const serverURLPrefix = "ServerUrl" const serverURLSuffixIterations = 10 +// serverURLPlaceholderRE captures `{name}` placeholders in a Server URL +// template. Per OpenAPI 3.0.3 §4.7.7, server-variable names are bound to +// the {name} tokens in `Server Object` URL templates; we treat anything +// matching `{[^/{}]+}` as a placeholder candidate. The character class +// excludes `/` so we don't accidentally span path segments, and `{`/`}` +// so nested braces (which the spec doesn't define anyway) don't merge. +var serverURLPlaceholderRE = regexp.MustCompile(`\{([^/{}]+)\}`) + +// urlPlaceholders returns the set of variable names referenced as +// `{name}` placeholders in a Server URL template, deduplicated. +func urlPlaceholders(url string) map[string]struct{} { + matches := serverURLPlaceholderRE.FindAllStringSubmatch(url, -1) + if len(matches) == 0 { + return nil + } + set := make(map[string]struct{}, len(matches)) + for _, m := range matches { + set[m[1]] = struct{}{} + } + return set +} + // ServerObjectDefinition defines the definition of an OpenAPI Server object (https://spec.openapis.org/oas/v3.0.3#server-object) as it is provided to code generation in `oapi-codegen` type ServerObjectDefinition struct { // GoName is the name of the variable for this Server URL @@ -20,16 +45,84 @@ type ServerObjectDefinition struct { OAPISchema *openapi3.Server } -func GenerateServerURLs(t *template.Template, spec *openapi3.T) (string, error) { +// UsedVariables returns the subset of OAPISchema.Variables whose +// `{name}` placeholder actually appears in OAPISchema.URL. Variables +// declared but unused are skipped — they would otherwise produce a +// type, constant, function parameter, and a no-op `strings.ReplaceAll` +// (https://github.com/oapi-codegen/oapi-codegen/issues/2004). Used by +// both server-urls.tmpl and BuildServerURLTypeDefinitions so that +// emitted types and the generated function signature stay in sync. +func (s ServerObjectDefinition) UsedVariables() map[string]*openapi3.ServerVariable { + if s.OAPISchema == nil || len(s.OAPISchema.Variables) == 0 { + return nil + } + placeholders := urlPlaceholders(s.OAPISchema.URL) + used := make(map[string]*openapi3.ServerVariable, len(s.OAPISchema.Variables)) + for name, v := range s.OAPISchema.Variables { + if _, ok := placeholders[name]; ok { + used[name] = v + } + } + return used +} + +// UndeclaredPlaceholders returns the sorted list of `{name}` +// placeholder names that appear in OAPISchema.URL but have no +// corresponding entry in OAPISchema.Variables. The previous code +// generated a function that referenced only declared variables, so +// any undeclared placeholder remained in the URL after substitution +// and the trailing `{`/`}` runtime check tripped on every call — +// making the generated function permanently unusable +// (https://github.com/oapi-codegen/oapi-codegen/issues/2005). The +// template now adds these as plain `string` parameters so callers +// can fill them in directly. +func (s ServerObjectDefinition) UndeclaredPlaceholders() []string { + if s.OAPISchema == nil { + return nil + } + placeholders := urlPlaceholders(s.OAPISchema.URL) + if len(placeholders) == 0 { + return nil + } + var undeclared []string + for name := range placeholders { + if _, declared := s.OAPISchema.Variables[name]; !declared { + undeclared = append(undeclared, name) + } + } + if len(undeclared) == 0 { + return nil + } + sort.Strings(undeclared) + return undeclared +} + +// serverObjectDefinitions deconflicts server names and returns the +// stable, deterministically-named ServerObjectDefinitions for `spec`. +// Used by both BuildServerURLTypeDefinitions and GenerateServerURLs so +// they generate identifiers that match. +func serverObjectDefinitions(spec *openapi3.T) ([]ServerObjectDefinition, error) { names := make(map[string]*openapi3.Server) for _, server := range spec.Servers { - suffix := server.Description - if suffix == "" { - suffix = nameNormalizer(server.URL) + var name string + if goNameExt, ok := server.Extensions[extGoName]; ok { + customName, err := extParseGoFieldName(goNameExt) + if err != nil { + return nil, fmt.Errorf("invalid value for %q: %w", extGoName, err) + } + if customName != "" { + name = customName + } + } + if name == "" { + suffix := server.Description + if suffix == "" { + suffix = nameNormalizer(server.URL) + } + name = serverURLPrefix + UppercaseFirstCharacter(suffix) + name = nameNormalizer(name) } - name := serverURLPrefix + UppercaseFirstCharacter(suffix) - name = nameNormalizer(name) // if this is the only type with this name, store it if _, conflict := names[name]; !conflict { @@ -37,20 +130,11 @@ func GenerateServerURLs(t *template.Template, spec *openapi3.T) (string, error) continue } - // otherwise, try appending a number to the name + // otherwise, try appending a number to the name. Start at 1 so + // `Foo` / `Foo1` reads better than `Foo` / `Foo0`. saved := false - // NOTE that we start at 1 on purpose, as - // - // ... ServerURLDevelopmentServer - // ... ServerURLDevelopmentServer1` - // - // reads better than: - // - // ... ServerURLDevelopmentServer - // ... ServerURLDevelopmentServer0 for i := 1; i < 1+serverURLSuffixIterations; i++ { suffixed := name + strconv.Itoa(i) - // and then store it if there's no conflict if _, suffixConflict := names[suffixed]; !suffixConflict { names[suffixed] = server saved = true @@ -62,20 +146,258 @@ func GenerateServerURLs(t *template.Template, spec *openapi3.T) (string, error) continue } - // otherwise, error - return "", fmt.Errorf("failed to create a unique name for the Server URL (%#v) with description (%#v) after %d iterations", server.URL, server.Description, serverURLSuffixIterations) + return nil, fmt.Errorf("failed to create a unique name for the Server URL (%#v) with description (%#v) after %d iterations", server.URL, server.Description, serverURLSuffixIterations) } keys := SortedMapKeys(names) servers := make([]ServerObjectDefinition, len(keys)) - i := 0 - for _, k := range keys { + for i, k := range keys { servers[i] = ServerObjectDefinition{ GoName: k, OAPISchema: names[k], } - i++ + } + return servers, nil +} + +// serverURLVariableTypeName returns the Go type identifier for the +// `enum`-typed variable `varName` on the server with deconflicted Go +// name `serverGoName`. Mirrors the naming scheme used by +// server-urls.tmpl (`%sVariable`) so that synthesized +// TypeDefinitions, the function signature emitted by the template, +// and any user references all resolve to the same identifier. +func serverURLVariableTypeName(serverGoName, varName string) string { + return serverGoName + UppercaseFirstCharacter(varName) + "Variable" +} + +// serverURLEnumKeys returns deterministic identifier suffixes for each +// enum value of `v`, in `v.Enum` order. Each suffix is just the value +// with its first character upper-cased — matching what the previous +// template-only path produced for happy-path specs (``), +// so adopters with non-colliding enums keep their existing identifiers. +// +// When two values fold to the same suffix (e.g. `enum: ["foo", "FOO"]`, +// both → `Foo`), later occurrences get a numeric suffix (`Foo`, `Foo1`, +// `Foo2`, …) — same scheme as `SanitizeEnumNames`. The previous +// template path didn't dedup at all and would have produced a +// duplicate-const compile error for these specs; this is purely a +// correctness improvement, not a naming change for any spec that +// actually compiled before. +func serverURLEnumKeys(v *openapi3.ServerVariable) []string { + keys := make([]string, len(v.Enum)) + seen := make(map[string]int, len(v.Enum)) + for i, val := range v.Enum { + base := UppercaseFirstCharacter(val) + if n, dup := seen[base]; dup { + keys[i] = base + strconv.Itoa(n) + seen[base] = n + 1 + } else { + keys[i] = base + seen[base] = 1 + } + } + return keys +} + +// serverURLEnumValues returns the EnumValues map handed to GenerateEnums +// for variable `v`: key is the deduplicated identifier suffix from +// serverURLEnumKeys, value is the original enum string. +func serverURLEnumValues(v *openapi3.ServerVariable) map[string]string { + keys := serverURLEnumKeys(v) + out := make(map[string]string, len(keys)) + for i, k := range keys { + out[k] = v.Enum[i] + } + return out +} + +// ServerURLDefaultPointer carries the pre-computed identifier names +// needed to emit a typed default-pointer constant for an enum-typed +// server-URL variable. Computed in Go (not the template) so that the +// pointer's reference to the enum-value constant always agrees with +// the identifier the const-block actually emitted, including in +// dedup-suffix cases (e.g. `enum: ["foo", "FOO"]` with `default: "FOO"` +// → enum const is `Foo1`, pointer must reference exactly that). +type ServerURLDefaultPointer struct { + // VariableName is the OpenAPI variable name (for doc comments). + VariableName string + // TypeName is the enum's Go type, e.g. `ServerUrlFooBarVariable`. + TypeName string + // PointerName is the constant being declared. Normally it is + // `Default` (matching the historical naming). Switches + // to `DefaultValue` only when the variable's enum + // contains a value whose identifier suffix is the literal string + // "Default" — i.e. exactly the case that produced #2003's + // duplicate-const compile error under the old codegen. Specs that + // compiled cleanly before keep their `Default` name. + PointerName string + // TargetName is the fully-qualified enum-value constant that the + // pointer references, e.g. `N443` or `Foo1`. + TargetName string + // Description is the variable's OpenAPI description (doc comment). + Description string +} + +// NewServerFunctionParams returns the formatted parameter list for +// the generated `New(...)` function — one typed parameter +// per declared-and-used variable, plus one `string` parameter per +// `{name}` placeholder that appears in the URL but isn't in +// `variables` (#2005). The two groups are sorted together +// alphabetically so the generated signature is deterministic. +// +// Equivalent to calling `genServerURLWithVariablesFunctionParams` for +// the typed half and concatenating the undeclared half — but exposing +// it as a method keeps server-urls.tmpl free of that combination +// logic and means the template helper itself stayed at its +// pre-existing two-argument shape (so any user-supplied custom +// `server-urls.tmpl` override that calls the helper directly is +// unaffected). +func (s ServerObjectDefinition) NewServerFunctionParams() string { + used := s.UsedVariables() + undeclared := s.UndeclaredPlaceholders() + if len(used) == 0 && len(undeclared) == 0 { + return "" + } + + type param struct { + name string + typ string + } + parts := make([]param, 0, len(used)+len(undeclared)) + for _, k := range SortedMapKeys(used) { + parts = append(parts, param{ + name: k, + typ: serverURLVariableTypeName(s.GoName, k), + }) + } + for _, k := range undeclared { + parts = append(parts, param{name: k, typ: "string"}) + } + sort.Slice(parts, func(i, j int) bool { return parts[i].name < parts[j].name }) + + out := make([]string, len(parts)) + for i, p := range parts { + out[i] = p.name + " " + p.typ + } + return strings.Join(out, ", ") +} + +// EnumDefaultPointers returns one entry per enum-typed used variable +// with a `default` set. The template iterates this directly rather +// than recomputing identifier names from `$v.Default`. +func (s ServerObjectDefinition) EnumDefaultPointers() []ServerURLDefaultPointer { + used := s.UsedVariables() + var out []ServerURLDefaultPointer + for _, varName := range SortedMapKeys(used) { + v := used[varName] + if v == nil || len(v.Enum) == 0 || v.Default == "" { + continue + } + keys := serverURLEnumKeys(v) + var targetKey string + for i, val := range v.Enum { + if val == v.Default { + targetKey = keys[i] + break + } + } + if targetKey == "" { + // `default` not in `enum`; BuildServerURLTypeDefinitions + // already errors on this at codegen time, so we'd never + // actually reach the template emission. Skip defensively. + continue + } + prefix := serverURLVariableTypeName(s.GoName, varName) + pointerName := prefix + "Default" + for _, k := range keys { + if k == "Default" { + pointerName = prefix + "DefaultValue" + break + } + } + out = append(out, ServerURLDefaultPointer{ + VariableName: varName, + TypeName: prefix, + PointerName: pointerName, + TargetName: prefix + targetKey, + Description: v.Description, + }) + } + return out +} + +// BuildServerURLTypeDefinitions synthesizes a TypeDefinition for every +// server-URL variable that defines an `enum`. These are appended into +// the same TypeDefinition slices used by GenerateTypes (typedef.tmpl) +// and GenerateEnums (constants.tmpl), so that server-URL enum +// variables get the same `type X string`, `const ( … )` block, and +// `Valid()` method as any other enum-bearing schema. The +// server-urls.tmpl template no longer emits these declarations +// directly. +// +// Variables without an `enum` are not handled here: server-urls.tmpl +// continues to emit their `type` and (optional) default constant +// inline, since the generic enum path has nothing to contribute for a +// non-enum string. +func BuildServerURLTypeDefinitions(spec *openapi3.T) ([]TypeDefinition, error) { + servers, err := serverObjectDefinitions(spec) + if err != nil { + return nil, err } + var defs []TypeDefinition + for _, srv := range servers { + used := srv.UsedVariables() + // Iterate variables in deterministic order. + for _, varName := range SortedMapKeys(used) { + v := used[varName] + if v == nil || len(v.Enum) == 0 { + continue + } + // Validate that `default`, if set, is one of the + // declared `enum` values. Per OpenAPI 3.0.3 §4.7.10, + // the default MUST be in the enum list when both are + // present; the previous template trusted the spec + // blindly and emitted a const referencing an + // undeclared identifier when the spec violated the + // rule, producing a confusing user-side compile error + // (https://github.com/oapi-codegen/oapi-codegen/issues/2007). + // Catch the violation at codegen time so the user sees + // a clear message pointing at their spec. + if v.Default != "" { + inEnum := false + for _, ev := range v.Enum { + if ev == v.Default { + inEnum = true + break + } + } + if !inEnum { + return nil, fmt.Errorf("server URL %q: variable %q has default value %q which is not one of the declared enum values %v", + srv.OAPISchema.URL, varName, v.Default, v.Enum) + } + } + typeName := serverURLVariableTypeName(srv.GoName, varName) + enumValues := serverURLEnumValues(v) + defs = append(defs, TypeDefinition{ + TypeName: typeName, + JsonName: varName, + Schema: Schema{ + GoType: "string", + EnumValues: enumValues, + Description: v.Description, + }, + ForceEnumPrefix: true, + }) + } + } + return defs, nil +} + +func GenerateServerURLs(t *template.Template, spec *openapi3.T) (string, error) { + servers, err := serverObjectDefinitions(spec) + if err != nil { + return "", err + } return GenerateTemplates([]string{"server-urls.tmpl"}, t, servers) } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/template_helpers.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/template_helpers.go index 49ee3aba6..3a91cfdeb 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/template_helpers.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/template_helpers.go @@ -17,6 +17,7 @@ import ( "bytes" "fmt" "os" + "slices" "strings" "text/template" @@ -24,6 +25,7 @@ import ( "golang.org/x/text/language" "github.com/getkin/kin-openapi/openapi3" + "github.com/oapi-codegen/oapi-codegen/v2/pkg/util" ) @@ -122,6 +124,7 @@ func genResponseUnmarshal(op *OperationDefinition) string { // Add a case for each possible response: buffer := new(bytes.Buffer) responses := op.Spec.Responses + handledResponseNames := make(map[string]struct{}) for _, typeDefinition := range typeDefinitions { responseRef := responses.Value(typeDefinition.ResponseName) @@ -135,6 +138,8 @@ func genResponseUnmarshal(op *OperationDefinition) string { continue } + handledResponseNames[typeDefinition.ResponseName] = struct{}{} + // If there is no content-type then we have no unmarshaling to do: if len(responseRef.Value.Content) == 0 { caseAction := "break // No content-type" @@ -147,7 +152,7 @@ func genResponseUnmarshal(op *OperationDefinition) string { SortedMapKeys := SortedMapKeys(responseRef.Value.Content) jsonCount := 0 for _, contentTypeName := range SortedMapKeys { - if StringInArray(contentTypeName, contentTypesJSON) || util.IsMediaTypeJson(contentTypeName) { + if slices.Contains(contentTypesJSON, contentTypeName) || util.IsMediaTypeJson(contentTypeName) { jsonCount++ } } @@ -164,7 +169,7 @@ func genResponseUnmarshal(op *OperationDefinition) string { switch { // JSON: - case StringInArray(contentTypeName, contentTypesJSON) || util.IsMediaTypeJson(contentTypeName): + case slices.Contains(contentTypesJSON, contentTypeName) || util.IsMediaTypeJson(contentTypeName): if typeDefinition.ContentTypeName == contentTypeName { caseAction := fmt.Sprintf("var dest %s\n"+ "if err := json.Unmarshal(bodyBytes, &dest); err != nil { \n"+ @@ -184,7 +189,7 @@ func genResponseUnmarshal(op *OperationDefinition) string { } // YAML: - case StringInArray(contentTypeName, contentTypesYAML): + case slices.Contains(contentTypesYAML, contentTypeName): if typeDefinition.ContentTypeName == contentTypeName { caseAction := fmt.Sprintf("var dest %s\n"+ "if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { \n"+ @@ -198,7 +203,7 @@ func genResponseUnmarshal(op *OperationDefinition) string { } // XML: - case StringInArray(contentTypeName, contentTypesXML): + case slices.Contains(contentTypesXML, contentTypeName): if typeDefinition.ContentTypeName == contentTypeName { caseAction := fmt.Sprintf("var dest %s\n"+ "if err := xml.Unmarshal(bodyBytes, &dest); err != nil { \n"+ @@ -220,6 +225,42 @@ func genResponseUnmarshal(op *OperationDefinition) string { } } + // Emit explicit case clauses for responses declared without content (e.g. + // "204 No Content"). These responses have no type definitions, so the loop + // above never visits them. Without an explicit case clause, the "default" + // catch-all (which matches with "&& true") would attempt to unmarshal an + // empty body and fail. + // + // Keys follow the same "." scheme as + // buildUnmarshalCase, so an exact bodyless code (e.g. "204") sorts among + // its numeric peers, a range wildcard (e.g. "2XX") sorts after every + // explicit content case it covers (preventing it from shadowing them) but + // before the default catch-all, which sorts last. + // + // "default" itself is skipped — a bodyless default would emit "case true:" + // which shadows everything. Such a spec is degenerate and the existing + // behaviour (no guard) is acceptable. + if responses != nil { + for _, responseName := range SortedMapKeys(responses.Map()) { + if _, handled := handledResponseNames[responseName]; handled { + continue + } + if responseName == "default" { + continue + } + responseRef := responses.Value(responseName) + if responseRef == nil || responseRef.Value == nil { + continue + } + if len(responseRef.Value.Content) == 0 { + caseCondition := getConditionOfResponseName("rsp.StatusCode", responseName) + caseClause := fmt.Sprintf("case %s:\nbreak // No content-type\n", caseCondition) + caseKey := fmt.Sprintf("%s.%s.nocontent", prefixLeastSpecific, responseName) + handledCaseClauses[caseKey] = caseClause + } + } + } + if len(handledCaseClauses)+len(unhandledCaseClauses) == 0 { // switch would be empty. return "" @@ -242,23 +283,38 @@ func genResponseUnmarshal(op *OperationDefinition) string { return buffer.String() } -// buildUnmarshalCase builds an unmarshaling case clause for different content-types: +// buildUnmarshalCase builds an unmarshaling case clause for different content-types. +// +// The sort key puts the response name before the content type so that +// lexicographic ordering of the keys yields most-specific-first case clauses: +// exact codes sort numerically ("200" < "204"), range wildcards sort after the +// exact codes they cover ("204" < "2XX" since 'X' > '9') and before the next +// tier ("2XX" < "300"), and "default" sorts after everything ('d' > 'X'). func buildUnmarshalCase(typeDefinition ResponseTypeDefinition, caseAction string, contentType string) (caseKey string, caseClause string) { - caseKey = fmt.Sprintf("%s.%s.%s", prefixLeastSpecific, contentType, typeDefinition.ResponseName) + caseKey = fmt.Sprintf("%s.%s.%s", prefixLeastSpecific, typeDefinition.ResponseName, contentType) caseClauseKey := getConditionOfResponseName("rsp.StatusCode", typeDefinition.ResponseName) - caseClause = fmt.Sprintf("case strings.Contains(rsp.Header.Get(\"%s\"), \"%s\") && %s:\n%s\n", "Content-Type", contentType, caseClauseKey, caseAction) + contentTypeLiteral := StringToGoString(contentType) + caseClause = fmt.Sprintf("case strings.Contains(rsp.Header.Get(\"%s\"), %s) && %s:\n%s\n", "Content-Type", contentTypeLiteral, caseClauseKey, caseAction) return caseKey, caseClause } +// buildUnmarshalCaseStrict is buildUnmarshalCase with an exact Content-Type +// match; it uses the same "." key scheme. func buildUnmarshalCaseStrict(typeDefinition ResponseTypeDefinition, caseAction string, contentType string) (caseKey string, caseClause string) { - caseKey = fmt.Sprintf("%s.%s.%s", prefixLeastSpecific, contentType, typeDefinition.ResponseName) + caseKey = fmt.Sprintf("%s.%s.%s", prefixLeastSpecific, typeDefinition.ResponseName, contentType) caseClauseKey := getConditionOfResponseName("rsp.StatusCode", typeDefinition.ResponseName) - caseClause = fmt.Sprintf("case rsp.Header.Get(\"%s\") == \"%s\" && %s:\n%s\n", "Content-Type", contentType, caseClauseKey, caseAction) + contentTypeLiteral := StringToGoString(contentType) + caseClause = fmt.Sprintf("case rsp.Header.Get(\"%s\") == %s && %s:\n%s\n", "Content-Type", contentTypeLiteral, caseClauseKey, caseAction) return caseKey, caseClause } -// genResponseTypeName creates the name of generated response types (given the operationID): +// genResponseTypeName creates the name of generated response types (given the operationID). +// It first checks if the multi-pass name resolver has assigned a name for this +// wrapper type (which would happen if the default name collides with a schema type). func genResponseTypeName(operationID string) string { + if name, ok := globalState.resolvedClientWrapperNames[operationID]; ok { + return name + } return fmt.Sprintf("%s%s", UppercaseFirstCharacter(operationID), responseTypeSuffix) } @@ -282,6 +338,24 @@ func getConditionOfResponseName(statusCodeVar, responseName string) string { } } +// responsesWithHeaders returns the subset of responses that declare headers, +// ordered most-specific-first for emission as switch case clauses: exact +// status codes sort numerically, range wildcards after the exact codes they +// cover ("204" < "2XX" since 'X' > '9'), and "default" (which compiles to +// `case true:`) after everything ('d' > 'X'). +func responsesWithHeaders(responses []ResponseDefinition) []ResponseDefinition { + var out []ResponseDefinition + for _, response := range responses { + if len(response.Headers) > 0 { + out = append(out, response) + } + } + slices.SortFunc(out, func(a, b ResponseDefinition) int { + return strings.Compare(a.StatusCode, b.StatusCode) + }) + return out +} + // This outputs a string array func toStringArray(sarr []string) string { s := strings.Join(sarr, `","`) @@ -291,8 +365,10 @@ func toStringArray(sarr []string) string { return `[]string{` + s + `}` } +// stripNewLines removes newlines so untrusted spec text stays inside a single +// generated `//` line comment instead of breaking out into real Go source. func stripNewLines(s string) string { - r := strings.NewReplacer("\n", "") + r := strings.NewReplacer("\n", "", "\r", "") return r.Replace(s) } @@ -300,6 +376,13 @@ func stripNewLines(s string) string { // // goTypePrefix is the prefix being used to create underlying types in the template (likely the `ServerObjectDefinition.GoName`) // variables are this `ServerObjectDefinition`'s variables for the Server object (likely the `ServerObjectDefinition.OAPISchema`) +// +// Undeclared `{name}` placeholders that appear in the URL but have no +// entry in `variables` are NOT handled here; they're emitted as plain +// `string` parameters by `ServerObjectDefinition.NewFunctionParams`, +// which the template calls instead of this helper. Custom +// `server-urls.tmpl` overrides that still call this helper directly +// keep their pre-existing two-argument signature. func genServerURLWithVariablesFunctionParams(goTypePrefix string, variables map[string]*openapi3.ServerVariable) string { keys := SortedMapKeys(variables) @@ -316,6 +399,31 @@ func genServerURLWithVariablesFunctionParams(goTypePrefix string, variables map[ return strings.Join(parts, ", ") } +// httpMethodConstant converts an HTTP method string (e.g. "GET") to the +// corresponding Go net/http constant (e.g. "http.MethodGet"). +func httpMethodConstant(method string) string { + switch method { + case "GET": + return "http.MethodGet" + case "POST": + return "http.MethodPost" + case "PUT": + return "http.MethodPut" + case "DELETE": + return "http.MethodDelete" + case "PATCH": + return "http.MethodPatch" + case "HEAD": + return "http.MethodHead" + case "OPTIONS": + return "http.MethodOptions" + case "TRACE": + return "http.MethodTrace" + default: + return fmt.Sprintf("%q", method) + } +} + // TemplateFunctions is passed to the template engine, and we can call each // function here by keyName from the template code. var TemplateFunctions = template.FuncMap{ @@ -337,13 +445,18 @@ var TemplateFunctions = template.FuncMap{ "genResponsePayload": genResponsePayload, "genResponseTypeName": genResponseTypeName, "genResponseUnmarshal": genResponseUnmarshal, + "getConditionOfResponseName": getConditionOfResponseName, + "responsesWithHeaders": responsesWithHeaders, "getResponseTypeDefinitions": getResponseTypeDefinitions, "toStringArray": toStringArray, "lower": strings.ToLower, "title": titleCaser.String, "stripNewLines": stripNewLines, "sanitizeGoIdentity": SanitizeGoIdentity, + "schemaNameToTypeName": SchemaNameToTypeName, + "toGoString": StringToGoString, "toGoComment": StringWithTypeNameToGoComment, "genServerURLWithVariablesFunctionParams": genServerURLWithVariablesFunctionParams, + "httpMethodConstant": httpMethodConstant, } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/additional-properties.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/additional-properties.tmpl index 2f450582d..a103216c9 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/additional-properties.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/additional-properties.tmpl @@ -53,12 +53,12 @@ func (a {{.TypeName}}) MarshalJSON() ([]byte, error) { var err error object := make(map[string]json.RawMessage) {{range .Schema.Properties}} -{{if .HasOptionalPointer}}if a.{{.GoFieldName}} != nil { {{end}} +{{if .RequiresNilCheck}}if a.{{.GoFieldName}} != nil { {{end}} object["{{.JsonFieldName}}"], err = json.Marshal(a.{{.GoFieldName}}) if err != nil { return nil, fmt.Errorf("error marshaling '{{.JsonFieldName}}': %w", err) } -{{if .HasOptionalPointer}} }{{end}} +{{if .RequiresNilCheck}} }{{end}} {{end}} for fieldName, field := range a.AdditionalProperties { object[fieldName], err = json.Marshal(field) diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/callback-initiator.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/callback-initiator.tmpl new file mode 100644 index 000000000..908695bf3 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/callback-initiator.tmpl @@ -0,0 +1,236 @@ +// CallbackInitiator sends OpenAPI callback requests to target URLs. +// Modeled on the generated Client, but with no stored Server -- the full +// target URL is provided per-call by the caller (typically discovered +// from a callback expression on the parent operation's request body, +// e.g. `{$request.body#/callbackUrl}`). +type CallbackInitiator struct { + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// CallbackInitiatorOption allows setting custom parameters during construction. +type CallbackInitiatorOption func(*CallbackInitiator) error + +// NewCallbackInitiator creates a new CallbackInitiator with reasonable defaults. +func NewCallbackInitiator(opts ...CallbackInitiatorOption) (*CallbackInitiator, error) { + initiator := CallbackInitiator{} + for _, o := range opts { + if err := o(&initiator); err != nil { + return nil, err + } + } + if initiator.Client == nil { + initiator.Client = &http.Client{} + } + return &initiator, nil +} + +// WithCallbackHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithCallbackHTTPClient(doer HttpRequestDoer) CallbackInitiatorOption { + return func(p *CallbackInitiator) error { + p.Client = doer + return nil + } +} + +// WithCallbackRequestEditorFn allows setting up a callback function, which +// will be called right before sending the callback request. This can be +// used to mutate the request, e.g. to add signature headers. +func WithCallbackRequestEditorFn(fn RequestEditorFn) CallbackInitiatorOption { + return func(p *CallbackInitiator) error { + p.RequestEditors = append(p.RequestEditors, fn) + return nil + } +} + +func (p *CallbackInitiator) applyCallbackEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range p.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// CallbackInitiatorInterface is the interface specification for the callback initiator. +type CallbackInitiatorInterface interface { +{{range . -}} +{{$hasParams := .RequiresParamObject -}} +{{$opid := .OperationId -}} + // {{$opid}}{{if .HasBody}}WithBody{{end}} fires the {{.CallbackName}} callback{{if .HasBody}} with any body{{end}} + {{$opid}}{{if .HasBody}}WithBody{{end}}(ctx context.Context, targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*http.Response, error) +{{range .Bodies}} + {{if .IsSupportedByClient -}} + {{$opid}}{{.Suffix}}(ctx context.Context, targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*http.Response, error) + {{end -}} +{{end}}{{/* range .Bodies */}} +{{end}}{{/* range . */}} +} + + +{{/* Generate callback initiator methods */}} +{{range . -}} +{{$hasParams := .RequiresParamObject -}} +{{$opid := .OperationId -}} + +func (p *CallbackInitiator) {{$opid}}{{if .HasBody}}WithBody{{end}}(ctx context.Context, targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*http.Response, error) { + req, err := New{{$opid}}CallbackRequest{{if .HasBody}}WithBody{{end}}(targetURL{{if $hasParams}}, params{{end}}{{if .HasBody}}, contentType, body{{end}}) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := p.applyCallbackEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return p.Client.Do(req) +} + +{{range .Bodies}} +{{if .IsSupportedByClient -}} +func (p *CallbackInitiator) {{$opid}}{{.Suffix}}(ctx context.Context, targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*http.Response, error) { + req, err := New{{$opid}}CallbackRequest{{.Suffix}}(targetURL{{if $hasParams}}, params{{end}}, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := p.applyCallbackEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return p.Client.Do(req) +} +{{end -}} +{{end}}{{/* range .Bodies */}} +{{end}}{{/* range . */}} + +{{/* Generate callback request builders */}} +{{range . -}} +{{$hasParams := .RequiresParamObject -}} +{{$opid := .OperationId -}} +{{$method := .Method -}} +{{$callbackName := .CallbackName -}} + +{{range .Bodies}} +{{if .IsSupportedByClient -}} +// New{{$opid}}CallbackRequest{{.Suffix}} builds a {{.ContentType}} {{$method}} request for the {{$callbackName}} callback +func New{{$opid}}CallbackRequest{{.Suffix}}(targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody) (*http.Request, error) { + var bodyReader io.Reader + {{if .IsJSON -}} + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + {{else if eq .NameTag "Formdata" -}} + bodyStr, err := runtime.MarshalForm(body, nil) + if err != nil { + return nil, err + } + bodyReader = strings.NewReader(bodyStr.Encode()) + {{else if eq .NameTag "Text" -}} + if stringer, ok := interface{}(body).(fmt.Stringer); ok { + bodyReader = strings.NewReader(stringer.String()) + } else { + {{if .Schema.IsPrimitive -}} + bodyReader = strings.NewReader(fmt.Sprint(body)) + {{else -}} + return nil, fmt.Errorf("text/plain is not supported for complex types, define a String() method on {{.Schema.TypeDecl}} to marshal it as text") + {{end -}} + } + {{end -}} + return New{{$opid}}CallbackRequestWithBody(targetURL{{if $hasParams}}, params{{end}}, "{{.ContentType}}", bodyReader) +} +{{end -}} +{{end}} + +// New{{$opid}}CallbackRequest{{if .HasBody}}WithBody{{end}} builds a {{.Method}} request for the {{.CallbackName}} callback{{if .HasBody}} with any body{{end}} +func New{{$opid}}CallbackRequest{{if .HasBody}}WithBody{{end}}(targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}) (*http.Request, error) { + var err error + _ = err + + reqURL, err := url.Parse(targetURL) + if err != nil { + return nil, err + } + +{{if .QueryParams}} + if params != nil { + queryValues := reqURL.Query() + var rawQueryFragments []string + {{range $paramIdx, $param := .QueryParams}} + {{if .RequiresNilCheck}} if params.{{.GoName}} != nil { {{end}} + {{if .IsPassThrough}} + queryValues.Add("{{.ParamName}}", {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}) + {{end}} + {{if .IsJson}} + if queryParamBuf, err := json.Marshal({{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}); err != nil { + return nil, err + } else { + queryValues.Add("{{.ParamName}}", string(queryParamBuf)) + } + {{end}} + {{if .IsStyled}} + if queryFrag, err := runtime.StyleParamWithOptions("{{.Style}}", {{.Explode}}, "{{.ParamName}}", {{if and .RequiresNilCheck .HasOptionalPointer}}*{{end}}params.{{.GoName}}, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + {{end}} + {{if .RequiresNilCheck}}}{{end}} + {{end}} + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + reqURL.RawQuery = strings.Join(rawQueryFragments, "&") + } +{{end}}{{/* if .QueryParams */}} + + req, err := http.NewRequest({{.Method | httpMethodConstant}}, reqURL.String(), {{if .HasBody}}body{{else}}nil{{end}}) + if err != nil { + return nil, err + } + + {{if .HasBody}}req.Header.Add("Content-Type", contentType){{end}} +{{ if .HeaderParams }} + if params != nil { + {{range $paramIdx, $param := .HeaderParams}} + {{if .RequiresNilCheck}} if params.{{.GoName}} != nil { {{end}} + var headerParam{{$paramIdx}} string + {{if .IsPassThrough}} + headerParam{{$paramIdx}} = {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}} + {{end}} + {{if .IsJson}} + var headerParamBuf{{$paramIdx}} []byte + headerParamBuf{{$paramIdx}}, err = json.Marshal({{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}) + if err != nil { + return nil, err + } + headerParam{{$paramIdx}} = string(headerParamBuf{{$paramIdx}}) + {{end}} + {{if .IsStyled}} + headerParam{{$paramIdx}}, err = runtime.StyleParamWithOptions("{{.Style}}", {{.Explode}}, "{{.ParamName}}", {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return nil, err + } + {{end}} + req.Header.Set("{{.ParamName}}", headerParam{{$paramIdx}}) + {{if .RequiresNilCheck}}}{{end}} + {{end}} + } +{{- end }}{{/* if .HeaderParams */}} + return req, nil +} + +{{end}}{{/* Range */}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-handler.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-handler.tmpl index 113e4d7ad..cc07375f9 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-handler.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-handler.tmpl @@ -43,7 +43,7 @@ ErrorHandlerFunc: options.ErrorHandlerFunc, } {{end}} {{range .}}r.Group(func(r chi.Router) { -r.{{.Method | lower | title }}(options.BaseURL+"{{.Path | swaggerUriToChiUri}}", wrapper.{{.OperationId}}) +r.{{.Method | lower | title }}(options.BaseURL+{{.Path | swaggerUriToChiUri | toGoString}}, wrapper.{{.HandlerName}}) }) {{end}} return r diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-interface.tmpl index cfc393bde..ba7e5e5fa 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-interface.tmpl @@ -1,17 +1,21 @@ // ServerInterface represents all server handlers. type ServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) -{{end}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}}{{end}} } // Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. type Unimplemented struct {} - {{range .}}{{.SummaryAsComment }} + {{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) - func (_ Unimplemented) {{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) { +{{with .DeprecationComment}}// +{{.}} +{{end}} func (_ Unimplemented) {{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) { w.WriteHeader(http.StatusNotImplemented) } - {{end}} \ No newline at end of file + {{end}}{{end}} \ No newline at end of file diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-middleware.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-middleware.tmpl index 97866fca6..b5980bb13 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-middleware.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-middleware.tmpl @@ -8,11 +8,12 @@ type ServerInterfaceWrapper struct { type MiddlewareFunc func(http.Handler) http.Handler {{range .}}{{$opid := .OperationId}} - +{{if not .IsAlias}} // {{$opid}} operation middleware func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Request) { {{if or .RequiresParamObject (gt (len .PathParams) 0) }} var err error + _ = err {{end}} {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" ------------- @@ -29,7 +30,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", chi.URLParam(r, "{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", chi.URLParam(r, "{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}", ValueIsUnescaped: r.URL.RawPath == ""}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -38,7 +39,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{end}} - {{if .SecurityDefinitions -}} + {{if and .SecurityDefinitions opts.Compatibility.EnableAuthScopesOnContext -}} ctx := r.Context() {{range .SecurityDefinitions}} ctx = context.WithValue(ctx, {{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes, {{toStringArray .Scopes}}) @@ -54,7 +55,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}} // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- {{ end }} - {{ if (or (or .Required .IsPassThrough) .IsJson) }} + {{ if (or .IsPassThrough .IsJson) }} if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" { {{if .IsPassThrough}} @@ -77,9 +78,14 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ }{{end}} {{end}} {{if .IsStyled}} - err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), ¶ms.{{.GoName}}) + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"}) + } else { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + } return } {{end}} @@ -98,7 +104,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ } {{if .IsPassThrough}} - params.{{.GoName}} = {{if .HasOptionalPointer }}&{{end}}valueList[0] + {{.GoName}} = valueList[0] {{end}} {{if .IsJson}} @@ -110,7 +116,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -159,7 +165,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{- if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -194,7 +200,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ handler.ServeHTTP(w, r) } -{{end}} +{{end}}{{end}} type UnescapedCookieParamError struct { ParamName string diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-receiver.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-receiver.tmpl new file mode 100644 index 000000000..3089faea4 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/chi/chi-receiver.tmpl @@ -0,0 +1,116 @@ +// {{.Prefix}}ReceiverInterface represents handlers for receiving inbound +// {{.PrefixLower}} requests. Each {{.PrefixLower}} becomes a Handle*{{.Prefix}} +// method that the implementation fills in. The caller mounts the per- +// {{.PrefixLower}} http.Handler returned by {Op}{{.Prefix}}Handler at +// whatever URL path they advertise to senders. +type {{.Prefix}}ReceiverInterface interface { +{{range .Operations -}} +{{.SummaryAsComment ""}} +// Handle{{.OperationId}}{{$.Prefix}} handles the {{.Method}} {{$.PrefixLower}} for {{.SourceName}}. +Handle{{.OperationId}}{{$.Prefix}}(w http.ResponseWriter, r *http.Request{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}} +} + +// {{.Prefix}}ReceiverMiddlewareFunc wraps an http.Handler with cross- +// cutting behavior (signature verification, logging, rate limiting, ...). +type {{.Prefix}}ReceiverMiddlewareFunc func(http.Handler) http.Handler + +{{range .Operations -}} +{{$opid := .OperationId -}} +{{$srcName := .SourceName -}} +// {{$opid}}{{$.Prefix}}Handler returns the http.Handler for the {{$srcName}} {{$.PrefixLower}}. +// Mount this at the URL path advertised to {{$.PrefixLower}} senders. errHandler +// may be nil; if so, parameter-binding errors return 400 with the error +// message. Middlewares are applied in the order provided -- the last +// argument becomes the outermost wrapper. +func {{$opid}}{{$.Prefix}}Handler(si {{$.Prefix}}ReceiverInterface, errHandler func(w http.ResponseWriter, r *http.Request, err error), middlewares ...{{$.Prefix}}ReceiverMiddlewareFunc) http.Handler { + if errHandler == nil { + errHandler = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +{{- if .RequiresParamObject}} + var err error + _ = err + + // Parameter object where we will unmarshal all parameters from the request. + var params {{$opid}}Params +{{range $paramIdx, $param := .QueryParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{if (or .IsPassThrough .IsJson)}} + if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + errHandler(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + errHandler(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"}) + return + }{{end}} + {{end}} + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + errHandler(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"}) + } else { + errHandler(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + } + return + } + {{end}} +{{end}} +{{range $paramIdx, $param := .HeaderParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := r.Header[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + errHandler(w, r, &TooManyValuesForParamError{ParamName: "{{.ParamName}}", Count: n}) + return + } + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{end}} + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + errHandler(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + errHandler(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + }{{if .Required}} else { + err := fmt.Errorf("Header parameter {{.ParamName}} is required, but not found") + errHandler(w, r, &RequiredHeaderError{ParamName: "{{.ParamName}}", Err: err}) + return + }{{end}} +{{end}} +{{- end}} + si.Handle{{$opid}}{{$.Prefix}}(w, r{{if .RequiresParamObject}}, params{{end}}) + }) + for _, mw := range middlewares { + h = mw(h) + } + return h +} + +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/client-with-responses.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/client-with-responses.tmpl index 3b85500a8..37fdd83b6 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/client-with-responses.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/client-with-responses.tmpl @@ -33,11 +33,26 @@ type ClientWithResponsesInterface interface { {{$hasParams := .RequiresParamObject -}} {{$pathParams := .PathParams -}} {{$opid := .OperationId -}} - // {{$opid}}{{if .HasBody}}WithBody{{end}}WithResponse request{{if .HasBody}} with any body{{end}} - {{$opid}}{{if .HasBody}}WithBody{{end}}WithResponse(ctx context.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*{{genResponseTypeName $opid}}, error) +{{$parent := . }} +{{$opidSuffix := ""}} +{{- if .HasBody -}} +{{- $opidSuffix = "WithBody" -}} +{{- end -}} +{{$deprecationComment := .DeprecationComment -}} + {{ if .HasBody }} + {{ .GenerateFunctionComment $opid "WithBodyWithResponse" true }} + {{ else }} + {{ .GenerateFunctionComment $opid "WithResponse" true }} + {{ end -}} + {{with $deprecationComment}}// + {{.}} + {{end -}}{{$opid}}{{if .HasBody}}WithBody{{end}}WithResponse(ctx context.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*{{genResponseTypeName $opid}}, error) {{range .Bodies}} {{if .IsSupportedByClient -}} - {{$opid}}{{.Suffix}}WithResponse(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*{{genResponseTypeName $opid}}, error) + {{ .GenerateFunctionComment $opid $parent (printf "%sWithResponse" .Suffix) true }} + {{with $deprecationComment}}// + {{.}} + {{end -}}{{$opid}}{{.Suffix}}WithResponse(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*{{genResponseTypeName $opid}}, error) {{end -}} {{end}}{{/* range .Bodies */}} {{end}}{{/* range . $opid := .OperationId */}} @@ -45,20 +60,43 @@ type ClientWithResponsesInterface interface { {{range .}}{{$opid := .OperationId}}{{$op := .}} {{$responseTypeDefinitions := getResponseTypeDefinitions .}} +{{$headerResponses := responsesWithHeaders .Responses}} +{{- range $headerResponses}} +// {{genResponseTypeName $opid | ucFirst}}{{.StatusCode | ucFirst}}Headers the declared response headers of an HTTP {{.StatusCode}} response for {{$opid}} +type {{genResponseTypeName $opid | ucFirst}}{{.StatusCode | ucFirst}}Headers struct { + {{range .Headers -}} + {{with .DeprecationComment}}{{.}} + {{end -}}{{.GoName}} {{.GoTypeDef}} + {{end -}} +} +{{end}} type {{genResponseTypeName $opid | ucFirst}} struct { Body []byte HTTPResponse *http.Response {{- range $responseTypeDefinitions}} + // {{.TypeName}} the response for an HTTP {{ .ResponseName }} `{{ .ContentTypeName }}` response {{.TypeName}} *{{.Schema.TypeDecl}} {{- end}} + {{- range $headerResponses}} + // Headers{{.StatusCode | ucFirst}} the parsed response headers for an HTTP {{.StatusCode}} response + Headers{{.StatusCode | ucFirst}} *{{genResponseTypeName $opid | ucFirst}}{{.StatusCode | ucFirst}}Headers + {{- end}} } +{{ if not opts.OutputOptions.SkipResponseBodyGetters }} {{- range $responseTypeDefinitions}} - {{- range .AdditionalTypeDefinitions}} - type {{.TypeName}} {{if .IsAlias }}={{end}} {{.Schema.TypeDecl}} - {{- end}} + // Get{{.TypeName}} returns the response for an HTTP {{ .ResponseName }} `{{ .ContentTypeName }}` response + func (r {{genResponseTypeName $opid | ucFirst}}) Get{{.TypeName}}() *{{.Schema.TypeDecl}} { + return r.{{.TypeName}} + } {{- end}} +// GetBody returns the raw response body bytes +func (r {{genResponseTypeName $opid | ucFirst}}) GetBody() []byte { + return r.Body +} +{{end}} + // Status returns HTTPResponse.Status func (r {{genResponseTypeName $opid | ucFirst}}) Status() string { if r.HTTPResponse != nil { @@ -81,15 +119,38 @@ func (r {{genResponseTypeName $opid | ucFirst}}) Bytes() []byte { return r.Body } {{end}} + +{{ if not opts.OutputOptions.SkipClientResponseContentType }} +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r {{genResponseTypeName $opid | ucFirst}}) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} +{{end}} + {{end}} {{range .}} {{$opid := .OperationId -}} +{{$parent := . }} +{{$opidSuffix := ""}} +{{- if .HasBody -}} +{{- $opidSuffix = "WithBody" -}} +{{- end -}} +{{$deprecationComment := .DeprecationComment -}} {{/* Generate client methods (with responses)*/}} -// {{$opid}}{{if .HasBody}}WithBody{{end}}WithResponse request{{if .HasBody}} with arbitrary body{{end}} returning *{{genResponseTypeName $opid}} -func (c *ClientWithResponses) {{$opid}}{{if .HasBody}}WithBody{{end}}WithResponse(ctx context.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*{{genResponseTypeName $opid}}, error){ +{{ if .HasBody }} + {{ .GenerateFunctionComment $opid "WithBodyWithResponse" true }} +{{ else }} + {{ .GenerateFunctionComment $opid "WithResponse" true }} +{{ end -}} +{{with $deprecationComment}}// +{{.}} +{{end -}}func (c *ClientWithResponses) {{$opid}}{{if .HasBody}}WithBody{{end}}WithResponse(ctx context.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*{{genResponseTypeName $opid}}, error){ rsp, err := c.{{$opid}}{{if .HasBody}}WithBody{{end}}(ctx{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}}{{if .HasBody}}, contentType, body{{end}}, reqEditors...) if err != nil { return nil, err @@ -102,7 +163,9 @@ func (c *ClientWithResponses) {{$opid}}{{if .HasBody}}WithBody{{end}}WithRespons {{$bodyRequired := .BodyRequired -}} {{range .Bodies}} {{if .IsSupportedByClient -}} -func (c *ClientWithResponses) {{$opid}}{{.Suffix}}WithResponse(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*{{genResponseTypeName $opid}}, error) { +{{ .GenerateFunctionComment $opid $parent (printf "%sWithResponse" .Suffix) true }} +{{with $deprecationComment}}{{.}} +{{end -}}func (c *ClientWithResponses) {{$opid}}{{.Suffix}}WithResponse(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*{{genResponseTypeName $opid}}, error) { rsp, err := c.{{$opid}}{{.Suffix}}(ctx{{genParamNames $pathParams}}{{if $hasParams}}, params{{end}}, body, reqEditors...) if err != nil { return nil, err @@ -128,6 +191,36 @@ func Parse{{genResponseTypeName $opid | ucFirst}}(rsp *http.Response) (*{{genRes response := {{genResponsePayload $opid}} {{genResponseUnmarshal .}} + {{- /* Bind declared response headers into the typed Headers + fields. Binding is lenient: an absent header (even a required one) leaves + the field at its zero value rather than failing the parse, mirroring the + body unmarshal's tolerance of spec-violating servers. A present header + that fails to bind to its declared type is an error. */ -}} + {{$headerResponses := responsesWithHeaders .Responses}} + {{- if $headerResponses}} + switch { + {{- range $headerResponses}} + case {{getConditionOfResponseName "rsp.StatusCode" .StatusCode}}: + var headers {{genResponseTypeName $opid | ucFirst}}{{.StatusCode | ucFirst}}Headers + {{- range .Headers}} + if values := rsp.Header.Values({{.Name | toGoString}}); len(values) > 0 { + var value {{.Schema.TypeDecl}} + if err := runtime.BindStyledParameterWithOptions("simple", {{.Name | toGoString}}, values[0], &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}); err != nil { + return nil, err + } + {{- if .IsNullable}} + headers.{{.GoName}}.Set(value) + {{- else if .IsOptional}} + headers.{{.GoName}} = &value + {{- else}} + headers.{{.GoName}} = value + {{- end}} + } + {{- end}} + response.Headers{{.StatusCode | ucFirst}} = &headers + {{- end}} + } + {{- end}} return response, nil } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/client.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/client.tmpl index 822e11097..55f298a4c 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/client.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/client.tmpl @@ -1,4 +1,4 @@ -// RequestEditorFn is the function signature for the RequestEditor callback function +// RequestEditorFn is the function signature for the RequestEditor callback function type RequestEditorFn func(ctx context.Context, req *http.Request) error // Doer performs HTTP requests. @@ -77,11 +77,26 @@ type ClientInterface interface { {{$hasParams := .RequiresParamObject -}} {{$pathParams := .PathParams -}} {{$opid := .OperationId -}} - // {{$opid}}{{if .HasBody}}WithBody{{end}} request{{if .HasBody}} with any body{{end}} - {{$opid}}{{if .HasBody}}WithBody{{end}}(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*http.Response, error) +{{$opidSuffix := ""}} +{{$parent := . }} +{{- if .HasBody -}} +{{- $opidSuffix = "WithBody" -}} +{{- end -}} +{{$deprecationComment := .DeprecationComment -}} + {{ if .HasBody }} + {{ .GenerateFunctionComment $opid "WithBody" false }} + {{ else -}} + {{ .GenerateFunctionComment $opid "" false }} + {{ end -}} + {{with $deprecationComment}}// + {{.}} + {{end -}}{{$opid}}{{if .HasBody}}WithBody{{end}}(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*http.Response, error) {{range .Bodies}} {{if .IsSupportedByClient -}} - {{$opid}}{{.Suffix}}(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*http.Response, error) + {{ .GenerateFunctionComment $opid $parent .Suffix false }} + {{with $deprecationComment}}// + {{.}} + {{end -}}{{$opid}}{{.Suffix}}(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*http.Response, error) {{end -}} {{end}}{{/* range .Bodies */}} {{end}}{{/* range . $opid := .OperationId */}} @@ -93,8 +108,19 @@ type ClientInterface interface { {{$hasParams := .RequiresParamObject -}} {{$pathParams := .PathParams -}} {{$opid := .OperationId -}} +{{$parent := . }} +{{$opidSuffix := ""}} +{{- if .HasBody -}} +{{- $opidSuffix = "WithBody" -}} +{{- end -}} -func (c *{{ $clientTypeName }}) {{$opid}}{{if .HasBody}}WithBody{{end}}(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*http.Response, error) { +{{ if .HasBody }} + {{ .GenerateFunctionComment $opid "WithBody" false }} +{{ else }} + {{ .GenerateFunctionComment $opid "" false }} +{{ end -}} +{{with .DeprecationComment}}{{.}} +{{end -}}func (c *{{ $clientTypeName }}) {{$opid}}{{if .HasBody}}WithBody{{end}}(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*http.Response, error) { req, err := New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(c.Server{{genParamNames .PathParams}}{{if $hasParams}}, params{{end}}{{if .HasBody}}, contentType, body{{end}}) if err != nil { return nil, err @@ -106,9 +132,12 @@ func (c *{{ $clientTypeName }}) {{$opid}}{{if .HasBody}}WithBody{{end}}(ctx cont return c.Client.Do(req) } +{{$deprecationComment := .DeprecationComment -}} {{range .Bodies}} {{if .IsSupportedByClient -}} -func (c *{{ $clientTypeName }}) {{$opid}}{{.Suffix}}(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*http.Response, error) { +{{ .GenerateFunctionComment $opid $parent .Suffix false }} +{{with $deprecationComment}}{{.}} +{{end -}}func (c *{{ $clientTypeName }}) {{$opid}}{{.Suffix}}(ctx context.Context{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*http.Response, error) { req, err := New{{$opid}}Request{{.Suffix}}(c.Server{{genParamNames $pathParams}}{{if $hasParams}}, params{{end}}, body) if err != nil { return nil, err @@ -148,14 +177,22 @@ func New{{$opid}}Request{{.Suffix}}(server string{{genParamArgs $pathParams}}{{i } bodyReader = strings.NewReader(bodyStr.Encode()) {{else if eq .NameTag "Text" -}} - bodyReader = strings.NewReader(string(body)) + if stringer, ok := interface{}(body).(fmt.Stringer); ok { + bodyReader = strings.NewReader(stringer.String()) + } else { + {{if .Schema.IsPrimitive -}} + bodyReader = strings.NewReader(fmt.Sprint(body)) + {{else -}} + return nil, fmt.Errorf("text/plain is not supported for complex types, define a String() method on {{.Schema.TypeDecl}} to marshal it as text") + {{end -}} + } {{end -}} - return New{{$opid}}RequestWithBody(server{{genParamNames $pathParams}}{{if $hasParams}}, params{{end}}, "{{.ContentType}}", bodyReader) + return New{{$opid}}RequestWithBody(server{{genParamNames $pathParams}}{{if $hasParams}}, params{{end}}, {{.ContentType | toGoString}}, bodyReader) } {{end -}} {{end}} -// New{{$opid}}Request{{if .HasBody}}WithBody{{end}} generates requests for {{$opid}}{{if .HasBody}} with any type of body{{end}} +// New{{$opid}}Request{{if .HasBody}}WithBody{{end}} constructs an http.Request for the {{$opid}} method{{if .HasBody}}, with any body, and a specified content type{{end}} func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamArgs $pathParams}}{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}) (*http.Request, error) { var err error {{range $paramIdx, $param := .PathParams}} @@ -172,7 +209,7 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr pathParam{{$paramIdx}} = string(pathParamBuf{{$paramIdx}}) {{end}} {{if .IsStyled}} - pathParam{{$paramIdx}}, err = runtime.StyleParamWithLocation("{{.Style}}", {{.Explode}}, "{{.ParamName}}", runtime.ParamLocationPath, {{.GoVariableName}}) + pathParam{{$paramIdx}}, err = runtime.StyleParamWithOptions("{{.Style}}", {{.Explode}}, "{{.ParamName}}", {{.GoVariableName}}, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return nil, err } @@ -195,9 +232,15 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr {{if .QueryParams}} if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string {{range $paramIdx, $param := .QueryParams}} - {{if .HasOptionalPointer}} if params.{{.GoName}} != nil { {{end}} + {{if .RequiresNilCheck}} if params.{{.GoName}} != nil { {{end}} {{if .IsPassThrough}} queryValues.Add("{{.ParamName}}", {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}) {{end}} @@ -210,24 +253,23 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr {{end}} {{if .IsStyled}} - if queryFrag, err := runtime.StyleParamWithLocation("{{.Style}}", {{.Explode}}, "{{.ParamName}}", runtime.ParamLocationQuery, {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}); err != nil { + if queryFrag, err := runtime.StyleParamWithOptions("{{.Style}}", {{.Explode}}, "{{.ParamName}}", {{if and .RequiresNilCheck .HasOptionalPointer}}*{{end}}params.{{.GoName}}, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}); err != nil { return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } } {{end}} - {{if .HasOptionalPointer}}}{{end}} + {{if .RequiresNilCheck}}}{{end}} {{end}} - queryURL.RawQuery = queryValues.Encode() + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") } {{end}}{{/* if .QueryParams */}} - req, err := http.NewRequest("{{.Method}}", queryURL.String(), {{if .HasBody}}body{{else}}nil{{end}}) + req, err := http.NewRequest({{.Method | httpMethodConstant}}, queryURL.String(), {{if .HasBody}}body{{else}}nil{{end}}) if err != nil { return nil, err } @@ -236,7 +278,7 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr {{ if .HeaderParams }} if params != nil { {{range $paramIdx, $param := .HeaderParams}} - {{if .HasOptionalPointer}} if params.{{.GoName}} != nil { {{end}} + {{if .RequiresNilCheck}} if params.{{.GoName}} != nil { {{end}} var headerParam{{$paramIdx}} string {{if .IsPassThrough}} headerParam{{$paramIdx}} = {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}} @@ -250,13 +292,13 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr headerParam{{$paramIdx}} = string(headerParamBuf{{$paramIdx}}) {{end}} {{if .IsStyled}} - headerParam{{$paramIdx}}, err = runtime.StyleParamWithLocation("{{.Style}}", {{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}) + headerParam{{$paramIdx}}, err = runtime.StyleParamWithOptions("{{.Style}}", {{.Explode}}, "{{.ParamName}}", {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return nil, err } {{end}} req.Header.Set("{{.ParamName}}", headerParam{{$paramIdx}}) - {{if .HasOptionalPointer}}}{{end}} + {{if .RequiresNilCheck}}}{{end}} {{end}} } {{- end }}{{/* if .HeaderParams */}} @@ -264,7 +306,7 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr {{ if .CookieParams }} if params != nil { {{range $paramIdx, $param := .CookieParams}} - {{if .HasOptionalPointer}} if params.{{.GoName}} != nil { {{end}} + {{if .RequiresNilCheck}} if params.{{.GoName}} != nil { {{end}} var cookieParam{{$paramIdx}} string {{if .IsPassThrough}} cookieParam{{$paramIdx}} = {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}} @@ -278,7 +320,7 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr cookieParam{{$paramIdx}} = url.QueryEscape(string(cookieParamBuf{{$paramIdx}})) {{end}} {{if .IsStyled}} - cookieParam{{$paramIdx}}, err = runtime.StyleParamWithLocation("simple", {{.Explode}}, "{{.ParamName}}", runtime.ParamLocationCookie, {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}) + cookieParam{{$paramIdx}}, err = runtime.StyleParamWithOptions("simple", {{.Explode}}, "{{.ParamName}}", {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationCookie, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return nil, err } @@ -288,7 +330,7 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr Value:cookieParam{{$paramIdx}}, } req.AddCookie(cookie{{$paramIdx}}) - {{if .HasOptionalPointer}}}{{end}} + {{if .RequiresNilCheck}}}{{end}} {{ end -}} } {{- end }}{{/* if .CookieParams */}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/constants.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/constants.tmpl index 8fad8764c..f09498c7e 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/constants.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/constants.tmpl @@ -1,7 +1,11 @@ -{{- if gt (len .SecuritySchemeProviderNames) 0 }} +{{- if and (gt (len .SecuritySchemeProviders) 0) opts.Compatibility.EnableAuthScopesOnContext }} const ( -{{range $ProviderName := .SecuritySchemeProviderNames}} - {{- $ProviderName | sanitizeGoIdentity | ucFirst}}Scopes = "{{$ProviderName}}.Scopes" +{{range $Provider := .SecuritySchemeProviders}} + {{- if $Provider.ImportedScopes}} + {{- $Provider.Name | sanitizeGoIdentity | ucFirst}}Scopes = {{$Provider.ImportedScopes}} + {{- else}} + {{- $Provider.Name | sanitizeGoIdentity | ucFirst}}Scopes {{$Provider.Name | schemaNameToTypeName | lcFirst}}ContextKey = "{{$Provider.Name}}.Scopes" + {{- end}} {{end}} ) {{end}} @@ -9,7 +13,18 @@ const ( // Defines values for {{$Enum.TypeName}}. const ( {{range $name, $value := $Enum.GetValues}} - {{$name}} {{$Enum.TypeName}} = {{$Enum.ValueWrapper}}{{$value}}{{$Enum.ValueWrapper -}} + {{$name}} {{$Enum.TypeName}} = {{if $Enum.ValueWrapper}}{{$value | toGoString}}{{else}}{{$value}}{{end -}} {{end}} ) +{{if not $.SkipEnumValidate}} +// Valid indicates whether the value is a known member of the {{$Enum.TypeName}} enum. +func (e {{$Enum.TypeName}}) Valid() bool { + switch e { + {{range $name, $value := $Enum.GetValues}}case {{$name}}: + return true + {{end}}default: + return false + } +} +{{end}} {{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-interface.tmpl index 738009169..f84da65a1 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-interface.tmpl @@ -1,7 +1,9 @@ // ServerInterface represents all server handlers. type ServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{.OperationId}}(ctx echo.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error -{{end}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{.OperationId}}(ctx echo.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error +{{end}}{{end}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-receiver.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-receiver.tmpl new file mode 100644 index 000000000..a90d03f83 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-receiver.tmpl @@ -0,0 +1,93 @@ +// {{.Prefix}}ReceiverInterface represents handlers for receiving inbound +// {{.PrefixLower}} requests. Each {{.PrefixLower}} becomes a Handle*{{.Prefix}} +// method that the implementation fills in. The caller mounts the per- +// {{.PrefixLower}} echo.HandlerFunc returned by {Op}{{.Prefix}}Handler at +// whatever URL path they advertise to senders. +type {{.Prefix}}ReceiverInterface interface { +{{range .Operations -}} +{{.SummaryAsComment ""}} +// Handle{{.OperationId}}{{$.Prefix}} handles the {{.Method}} {{$.PrefixLower}} for {{.SourceName}}. +Handle{{.OperationId}}{{$.Prefix}}(ctx echo.Context{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error +{{end}} +} + +{{range .Operations -}} +{{$opid := .OperationId -}} +{{$srcName := .SourceName -}} +// {{$opid}}{{$.Prefix}}Handler returns the echo.HandlerFunc for the {{$srcName}} {{$.PrefixLower}}. +// Mount this at the URL path advertised to {{$.PrefixLower}} senders. Errors +// during parameter binding are returned via echo.NewHTTPError so echo's +// error-handling chain reports them as 400. Middlewares are applied in +// the order provided -- the last argument becomes the outermost wrapper. +func {{$opid}}{{$.Prefix}}Handler(si {{$.Prefix}}ReceiverInterface, middlewares ...echo.MiddlewareFunc) echo.HandlerFunc { + h := echo.HandlerFunc(func(ctx echo.Context) error { +{{- if .RequiresParamObject}} + var err error + _ = err + + // Parameter object where we will unmarshal all parameters from the context. + var params {{$opid}}Params +{{range $paramIdx, $param := .QueryParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.QueryParams(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + } + {{else}} + if paramValue := ctx.QueryParam("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Error unmarshaling parameter '{{.ParamName}}' as JSON") + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Query argument {{.ParamName}} is required, but not found")) + }{{end}} + {{end}} +{{end}} +{{range $paramIdx, $param := .HeaderParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := ctx.Request().Header[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n)) + } + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{end}} + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Error unmarshaling parameter '{{.ParamName}}' as JSON") + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + }{{if .Required}} else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Header parameter {{.ParamName}} is required, but not found")) + }{{end}} +{{end}} +{{- end}} + return si.Handle{{$opid}}{{$.Prefix}}(ctx{{if .RequiresParamObject}}, params{{end}}) + }) + for _, mw := range middlewares { + h = mw(h) + } + return h +} + +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-register.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-register.tmpl index 21b52fe48..d161fca37 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-register.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-register.tmpl @@ -15,19 +15,38 @@ type EchoRouter interface { TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route } +// RegisterHandlersOptions configures RegisterHandlersWithOptions. +type RegisterHandlersOptions struct { + // BaseURL is prepended to every registered path so the API can be served + // under a prefix. + BaseURL string + // OperationMiddlewares lets the caller attach per-operation middleware at + // registration time. The map key is the OpenAPI `operationId` value as it + // appears in the spec (the raw, un-normalized form). Operations that have + // no entry are registered with no extra middleware. A nil map disables + // per-operation middleware entirely. + OperationMiddlewares map[string][]echo.MiddlewareFunc +} + // RegisterHandlers adds each server route to the EchoRouter. func RegisterHandlers(router EchoRouter, si ServerInterface) { - RegisterHandlersWithBaseURL(router, si, "") + RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{}) } -// Registers handlers, and prepends BaseURL to the paths, so that the paths -// can be served under a prefix. +// RegisterHandlersWithBaseURL registers handlers and prepends BaseURL to the +// paths so the API can be served under a prefix. func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{BaseURL: baseURL}) +} + +// RegisterHandlersWithOptions registers handlers using the supplied options, +// including any per-operation middleware. +func RegisterHandlersWithOptions(router EchoRouter, si ServerInterface, options RegisterHandlersOptions) { {{if .}} wrapper := ServerInterfaceWrapper{ Handler: si, } {{end}} -{{range .}}router.{{.Method}}(baseURL + "{{.Path | swaggerUriToEchoUri}}", wrapper.{{.OperationId}}) +{{range .}}router.{{.Method}}(options.BaseURL + {{.Path | swaggerUriToEchoUri | toGoString}}, wrapper.{{.HandlerName}}, options.OperationMiddlewares["{{.MiddlewareKey}}"]...) {{end}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-wrappers.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-wrappers.tmpl index ea7b75eaa..263dca089 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-wrappers.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/echo-wrappers.tmpl @@ -3,7 +3,7 @@ type ServerInterfaceWrapper struct { Handler ServerInterface } -{{range .}}{{$opid := .OperationId}}// {{$opid}} converts echo context to params. +{{range .}}{{$opid := .OperationId}}{{if not .IsAlias}}// {{$opid}} converts echo context to params. func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { var err error {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" ------------- @@ -18,16 +18,18 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", ctx.Param("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", ctx.Param("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) } {{end}} {{end}} +{{if opts.Compatibility.EnableAuthScopesOnContext -}} {{range .SecurityDefinitions}} - ctx.Set({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes, {{toStringArray .Scopes}}) + ctx.Set(string({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes), {{toStringArray .Scopes}}) {{end}} +{{- end}} {{if .RequiresParamObject}} // Parameter object where we will unmarshal all parameters from the context @@ -37,7 +39,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- {{ end }} {{if .IsStyled}} - err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.QueryParams(), ¶ms.{{.GoName}}) + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.QueryParams(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) } @@ -70,7 +72,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n)) } {{if .IsPassThrough}} - params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{.GoName}} = valueList[0] {{end}} {{if .IsJson}} err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) @@ -79,7 +81,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) } @@ -111,7 +113,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { {{end}} {{if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) } @@ -128,4 +130,4 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { err = w.Handler.{{.OperationId}}(ctx{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}}) return err } -{{end}} +{{end}}{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-interface.tmpl new file mode 100644 index 000000000..d0997e5c6 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-interface.tmpl @@ -0,0 +1,9 @@ +// ServerInterface represents all server handlers. +type ServerInterface interface { +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} +// ({{.Method}} {{.Path}}) +{{with .DeprecationComment}}// +{{.}} +{{end}}{{.OperationId}}(ctx *echo.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error +{{end}}{{end}} +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-receiver.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-receiver.tmpl new file mode 100644 index 000000000..b0f10d5b1 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-receiver.tmpl @@ -0,0 +1,93 @@ +// {{.Prefix}}ReceiverInterface represents handlers for receiving inbound +// {{.PrefixLower}} requests. Each {{.PrefixLower}} becomes a Handle*{{.Prefix}} +// method that the implementation fills in. The caller mounts the per- +// {{.PrefixLower}} echo.HandlerFunc returned by {Op}{{.Prefix}}Handler at +// whatever URL path they advertise to senders. +type {{.Prefix}}ReceiverInterface interface { +{{range .Operations -}} +{{.SummaryAsComment ""}} +// Handle{{.OperationId}}{{$.Prefix}} handles the {{.Method}} {{$.PrefixLower}} for {{.SourceName}}. +Handle{{.OperationId}}{{$.Prefix}}(ctx *echo.Context{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error +{{end}} +} + +{{range .Operations -}} +{{$opid := .OperationId -}} +{{$srcName := .SourceName -}} +// {{$opid}}{{$.Prefix}}Handler returns the echo.HandlerFunc for the {{$srcName}} {{$.PrefixLower}}. +// Mount this at the URL path advertised to {{$.PrefixLower}} senders. Errors +// during parameter binding are returned via echo.NewHTTPError so echo's +// error-handling chain reports them as 400. Middlewares are applied in +// the order provided -- the last argument becomes the outermost wrapper. +func {{$opid}}{{$.Prefix}}Handler(si {{$.Prefix}}ReceiverInterface, middlewares ...echo.MiddlewareFunc) echo.HandlerFunc { + h := echo.HandlerFunc(func(ctx *echo.Context) error { +{{- if .RequiresParamObject}} + var err error + _ = err + + // Parameter object where we will unmarshal all parameters from the context. + var params {{$opid}}Params +{{range $paramIdx, $param := .QueryParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.QueryParams(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + } + {{else}} + if paramValue := ctx.QueryParam("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Error unmarshaling parameter '{{.ParamName}}' as JSON") + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Query argument {{.ParamName}} is required, but not found")) + }{{end}} + {{end}} +{{end}} +{{range $paramIdx, $param := .HeaderParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := ctx.Request().Header[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n)) + } + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{end}} + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Error unmarshaling parameter '{{.ParamName}}' as JSON") + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + }{{if .Required}} else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Header parameter {{.ParamName}} is required, but not found")) + }{{end}} +{{end}} +{{- end}} + return si.Handle{{$opid}}{{$.Prefix}}(ctx{{if .RequiresParamObject}}, params{{end}}) + }) + for _, mw := range middlewares { + h = mw(h) + } + return h +} + +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-register.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-register.tmpl new file mode 100644 index 000000000..95ccf3231 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-register.tmpl @@ -0,0 +1,52 @@ + + +// This is a simple interface which specifies echo.Route addition functions which +// are present on both echo.Echo and echo.Group, since we want to allow using +// either of them for path registration +type EchoRouter interface { + CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo +} + +// RegisterHandlersOptions configures RegisterHandlersWithOptions. +type RegisterHandlersOptions struct { + // BaseURL is prepended to every registered path so the API can be served + // under a prefix. + BaseURL string + // OperationMiddlewares lets the caller attach per-operation middleware at + // registration time. The map key is the OpenAPI `operationId` value as it + // appears in the spec (the raw, un-normalized form). Operations that have + // no entry are registered with no extra middleware. A nil map disables + // per-operation middleware entirely. + OperationMiddlewares map[string][]echo.MiddlewareFunc +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{}) +} + +// RegisterHandlersWithBaseURL registers handlers and prepends BaseURL to the +// paths so the API can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{BaseURL: baseURL}) +} + +// RegisterHandlersWithOptions registers handlers using the supplied options, +// including any per-operation middleware. +func RegisterHandlersWithOptions(router EchoRouter, si ServerInterface, options RegisterHandlersOptions) { +{{if .}} + wrapper := ServerInterfaceWrapper{ + Handler: si, + } +{{end}} +{{range .}}router.{{.Method}}(options.BaseURL + {{.Path | swaggerUriToEchoUri | toGoString}}, wrapper.{{.HandlerName}}, options.OperationMiddlewares["{{.MiddlewareKey}}"]...) +{{end}} +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-wrappers.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-wrappers.tmpl new file mode 100644 index 000000000..3d690b384 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/echo/v5/echo-wrappers.tmpl @@ -0,0 +1,133 @@ +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +{{range .}}{{$opid := .OperationId}}{{if not .IsAlias}}// {{$opid}} converts echo context to params. +func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx *echo.Context) error { + var err error +{{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" ------------- + var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}} +{{if .IsPassThrough}} + {{$varName}} = ctx.Param("{{.ParamName}}") +{{end}} +{{if .IsJson}} + err = json.Unmarshal([]byte(ctx.Param("{{.ParamName}}")), &{{$varName}}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Error unmarshaling parameter '{{.ParamName}}' as JSON") + } +{{end}} +{{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", ctx.Param("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + } +{{end}} +{{end}} + +{{if opts.Compatibility.EnableAuthScopesOnContext -}} +{{range .SecurityDefinitions}} + ctx.Set(string({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes), {{toStringArray .Scopes}}) +{{end}} +{{- end}} + +{{if .RequiresParamObject}} + // Parameter object where we will unmarshal all parameters from the context + var params {{.OperationId}}Params +{{range $paramIdx, $param := .QueryParams}} + {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{ end }} + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.QueryParams(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + } + {{else}} + if paramValue := ctx.QueryParam("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Error unmarshaling parameter '{{.ParamName}}' as JSON") + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Query argument {{.ParamName}} is required, but not found")) + }{{end}} + {{end}} +{{end}} + +{{if .HeaderParams}} + headers := ctx.Request().Header +{{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n)) + } +{{if .IsPassThrough}} + {{.GoName}} = valueList[0] +{{end}} +{{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Error unmarshaling parameter '{{.ParamName}}' as JSON") + } +{{end}} +{{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + } +{{end}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + } {{if .Required}}else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Header parameter {{.ParamName}} is required, but not found")) + }{{end}} +{{end}} +{{end}} + +{{range .CookieParams}} + if cookie, err := ctx.Cookie("{{.ParamName}}"); err == nil { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}cookie.Value + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + var decoded string + decoded, err := url.QueryUnescape(cookie.Value) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Error unescaping cookie parameter '{{.ParamName}}'") + } + err = json.Unmarshal([]byte(decoded), &value) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Error unmarshaling parameter '{{.ParamName}}' as JSON") + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + {{if .IsStyled}} + var value {{.TypeDef}} + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Query argument {{.ParamName}} is required, but not found")) + }{{end}} + +{{end}}{{/* .CookieParams */}} + +{{end}}{{/* .RequiresParamObject */}} + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.{{.OperationId}}(ctx{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}}) + return err +} +{{end}}{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-handler.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-handler.tmpl new file mode 100644 index 000000000..8460db83c --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-handler.tmpl @@ -0,0 +1,27 @@ +// FiberServerOptions provides options for the Fiber server. +type FiberServerOptions struct { + BaseURL string + Middlewares []MiddlewareFunc + HandlerMiddlewares []HandlerMiddlewareFunc +} + +// RegisterHandlers creates http.Handler with routing matching OpenAPI spec. +func RegisterHandlers(router fiber.Router, si ServerInterface) { + RegisterHandlersWithOptions(router, si, FiberServerOptions{}) +} + +// RegisterHandlersWithOptions creates http.Handler with additional options +func RegisterHandlersWithOptions(router fiber.Router, si ServerInterface, options FiberServerOptions) { +{{if .}}wrapper := ServerInterfaceWrapper{ +Handler: si, +HandlerMiddlewares: options.HandlerMiddlewares, +} + +for _, m := range options.Middlewares { + router.Use(fiber.Handler(m)) +} +{{end}} +{{range .}} +router.{{.Method | lower | title }}(options.BaseURL+{{.Path | swaggerUriToFiberUri | toGoString}}, wrapper.{{.HandlerName}}) +{{end}} +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-interface.tmpl new file mode 100644 index 000000000..466e98e52 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-interface.tmpl @@ -0,0 +1,9 @@ +// ServerInterface represents all server handlers. +type ServerInterface interface { +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} +// ({{.Method}} {{.Path}}) +{{with .DeprecationComment}}// +{{.}} +{{end}}{{.OperationId}}(c fiber.Ctx{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error +{{end}}{{end}} +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-middleware.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-middleware.tmpl new file mode 100644 index 000000000..042a15802 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-middleware.tmpl @@ -0,0 +1,198 @@ +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []HandlerMiddlewareFunc +} + +type MiddlewareFunc fiber.Handler +type HandlerMiddlewareFunc func(c fiber.Ctx, next fiber.Handler) error + +{{range .}}{{$opid := .OperationId}} +{{if not .IsAlias}} +// {{$opid}} operation middleware +func (siw *ServerInterfaceWrapper) {{$opid}}(c fiber.Ctx) error { + + {{if or .RequiresParamObject (gt (len .PathParams) 0) }} + var err error + _ = err + {{end}} + + {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" ------------- + var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}} + + {{if .IsPassThrough}} + {{$varName}}, err = url.PathUnescape(c.Params("{{.ParamName}}")) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unescaping path parameter '{{.ParamName}}': %w", err).Error()) + } + {{end}} + {{if .IsJson}} + { + paramValue, decErr := url.PathUnescape(c.Params("{{.ParamName}}")) + if decErr != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unescaping path parameter '{{.ParamName}}': %w", decErr).Error()) + } + err = json.Unmarshal([]byte(paramValue), &{{$varName}}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + } + } + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", c.Params("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) + } + {{end}} + + {{end}} + +{{if opts.Compatibility.EnableAuthScopesOnContext -}} +{{range .SecurityDefinitions}} + c.RequestCtx().SetUserValue(({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes), {{toStringArray .Scopes}}) +{{end}} +{{- end}} + + {{if .RequiresParamObject}} + // Parameter object where we will unmarshal all parameters from the context + var params {{.OperationId}}Params + + {{if .QueryParams}} + var query url.Values + query, err = url.ParseQuery(string(c.Request().URI().QueryString())) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for query string: %w", err).Error()) + } + {{end}} + + {{range $paramIdx, $param := .QueryParams}} + {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{ end }} + {{ if (or .IsPassThrough .IsJson) }} + if paramValue := c.Query("{{.ParamName}}"); paramValue != "" { + + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + } + + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + return fiber.NewError(fiber.StatusBadRequest, "Query argument {{.ParamName}} is required, but not found") + }{{end}} + {{end}} + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", query, ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) + } + {{end}} + {{end}} + + {{if .HeaderParams}} + headers := c.GetReqHeaders() + + {{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Too many values for ParamName {{.ParamName}}, 1 is required, but %d found", n)) + } + + {{if .IsPassThrough}} + {{.GoName}} = valueList[0] + {{end}} + + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + } + {{end}} + + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) + } + {{end}} + + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + + } {{if .Required}}else { + return fiber.NewError(fiber.StatusBadRequest, "Header parameter {{.ParamName}} is required, but not found") + }{{end}} + + {{end}} + {{end}} + + {{range .CookieParams}} + { + cookie := c.Cookies("{{.ParamName}}") + + if cookie != "" { + + {{- if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}cookie + {{end}} + + {{- if .IsJson}} + var value {{.TypeDef}} + var decoded string + decoded, err := url.QueryUnescape(cookie) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unescaping cookie parameter '{{.ParamName}}': %w", err).Error()) + } + + err = json.Unmarshal([]byte(decoded), &value) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + } + + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + + {{- if .IsStyled}} + var value {{.TypeDef}} + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + + } + + {{- if .Required}} else { + err = fmt.Errorf("Query argument {{.ParamName}} is required, but not found") + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + {{- end}} + } + {{end}} + {{end}} + + handler := func(c fiber.Ctx) error { + return siw.Handler.{{.OperationId}}(c{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}}) + } + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + m := siw.HandlerMiddlewares[i] + next := handler + handler = func(c fiber.Ctx) error { + return m(c, next) + } + } + + return handler(c) +} +{{end}}{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-receiver.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-receiver.tmpl new file mode 100644 index 000000000..f28d2bdf4 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber-v3/fiber-receiver.tmpl @@ -0,0 +1,100 @@ +// {{.Prefix}}ReceiverInterface represents handlers for receiving inbound +// {{.PrefixLower}} requests. Each {{.PrefixLower}} becomes a Handle*{{.Prefix}} +// method that the implementation fills in. The caller mounts the per- +// {{.PrefixLower}} fiber.Handler returned by {Op}{{.Prefix}}Handler at +// whatever URL path they advertise to senders. +type {{.Prefix}}ReceiverInterface interface { +{{range .Operations -}} +{{.SummaryAsComment ""}} +// Handle{{.OperationId}}{{$.Prefix}} handles the {{.Method}} {{$.PrefixLower}} for {{.SourceName}}. +Handle{{.OperationId}}{{$.Prefix}}(c fiber.Ctx{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error +{{end}} +} + +{{range .Operations -}} +{{$opid := .OperationId -}} +{{$srcName := .SourceName -}} +// {{$opid}}{{$.Prefix}}Handler returns the fiber.Handler for the {{$srcName}} {{$.PrefixLower}}. +// Mount this at the URL path advertised to {{$.PrefixLower}} senders. Errors +// during parameter binding are returned via fiber.NewError so fiber's +// error chain reports them as 400. Per-handler middleware is not +// generated here; use fiber.App.Use() for engine-level middleware. +func {{$opid}}{{$.Prefix}}Handler(si {{$.Prefix}}ReceiverInterface) fiber.Handler { + return func(c fiber.Ctx) error { +{{- if .RequiresParamObject}} + var err error + _ = err + + // Parameter object where we will unmarshal all parameters from the context. + var params {{$opid}}Params +{{if .QueryParams}} + var query url.Values + query, err = url.ParseQuery(string(c.Request().URI().QueryString())) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for query string: %w", err).Error()) + } +{{end}} +{{range $paramIdx, $param := .QueryParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", query, ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) + } + {{else}} + if paramValue := c.Query("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + return fiber.NewError(fiber.StatusBadRequest, "Query argument {{.ParamName}} is required, but not found") + }{{end}} + {{end}} +{{end}} +{{if .HeaderParams}} + headers := c.GetReqHeaders() +{{range $paramIdx, $param := .HeaderParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Too many values for header {{.ParamName}}, 1 is required, but %d found", n)) + } + {{if .IsPassThrough}} + {{.GoName}} = valueList[0] + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + }{{if .Required}} else { + return fiber.NewError(fiber.StatusBadRequest, "Header parameter {{.ParamName}} is required, but not found") + }{{end}} +{{end}} +{{end}} +{{- end}} + return si.Handle{{$opid}}{{$.Prefix}}(c{{if .RequiresParamObject}}, params{{end}}) + } +} + +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-handler.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-handler.tmpl index 7745e0d88..8460db83c 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-handler.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-handler.tmpl @@ -2,6 +2,7 @@ type FiberServerOptions struct { BaseURL string Middlewares []MiddlewareFunc + HandlerMiddlewares []HandlerMiddlewareFunc } // RegisterHandlers creates http.Handler with routing matching OpenAPI spec. @@ -13,6 +14,7 @@ func RegisterHandlers(router fiber.Router, si ServerInterface) { func RegisterHandlersWithOptions(router fiber.Router, si ServerInterface, options FiberServerOptions) { {{if .}}wrapper := ServerInterfaceWrapper{ Handler: si, +HandlerMiddlewares: options.HandlerMiddlewares, } for _, m := range options.Middlewares { @@ -20,6 +22,6 @@ for _, m := range options.Middlewares { } {{end}} {{range .}} -router.{{.Method | lower | title }}(options.BaseURL+"{{.Path | swaggerUriToFiberUri}}", wrapper.{{.OperationId}}) +router.{{.Method | lower | title }}(options.BaseURL+{{.Path | swaggerUriToFiberUri | toGoString}}, wrapper.{{.HandlerName}}) {{end}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-interface.tmpl index 8ef90a851..94c90c086 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-interface.tmpl @@ -1,7 +1,9 @@ // ServerInterface represents all server handlers. type ServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{.OperationId}}(c *fiber.Ctx{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error -{{end}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{.OperationId}}(c *fiber.Ctx{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error +{{end}}{{end}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-middleware.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-middleware.tmpl index e44f4836c..9766d074f 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-middleware.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-middleware.tmpl @@ -1,33 +1,45 @@ // ServerInterfaceWrapper converts contexts to parameters. type ServerInterfaceWrapper struct { Handler ServerInterface + HandlerMiddlewares []HandlerMiddlewareFunc } type MiddlewareFunc fiber.Handler +type HandlerMiddlewareFunc func(c *fiber.Ctx, next fiber.Handler) error {{range .}}{{$opid := .OperationId}} - +{{if not .IsAlias}} // {{$opid}} operation middleware func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { {{if or .RequiresParamObject (gt (len .PathParams) 0) }} var err error + _ = err {{end}} {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" ------------- var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}} {{if .IsPassThrough}} - {{$varName}} = c.Query("{{.ParamName}}") + {{$varName}}, err = url.PathUnescape(c.Params("{{.ParamName}}")) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unescaping path parameter '{{.ParamName}}': %w", err).Error()) + } {{end}} {{if .IsJson}} - err = json.Unmarshal([]byte(c.Query("{{.ParamName}}")), &{{$varName}}) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + { + paramValue, decErr := url.PathUnescape(c.Params("{{.ParamName}}")) + if decErr != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unescaping path parameter '{{.ParamName}}': %w", decErr).Error()) + } + err = json.Unmarshal([]byte(paramValue), &{{$varName}}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + } } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", c.Params("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", c.Params("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) } @@ -35,9 +47,11 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { {{end}} +{{if opts.Compatibility.EnableAuthScopesOnContext -}} {{range .SecurityDefinitions}} - c.Context().SetUserValue({{.ProviderName | ucFirst}}Scopes, {{toStringArray .Scopes}}) + c.Context().SetUserValue(({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes), {{toStringArray .Scopes}}) {{end}} +{{- end}} {{if .RequiresParamObject}} // Parameter object where we will unmarshal all parameters from the context @@ -55,7 +69,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}} // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- {{ end }} - {{ if (or (or .Required .IsPassThrough) .IsJson) }} + {{ if (or .IsPassThrough .IsJson) }} if paramValue := c.Query("{{.ParamName}}"); paramValue != "" { {{if .IsPassThrough}} @@ -72,13 +86,11 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value {{end}} }{{if .Required}} else { - err = fmt.Errorf("Query argument {{.ParamName}} is required, but not found") - c.Status(fiber.StatusBadRequest).JSON(err) - return err + return fiber.NewError(fiber.StatusBadRequest, "Query argument {{.ParamName}} is required, but not found") }{{end}} {{end}} {{if .IsStyled}} - err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", query, ¶ms.{{.GoName}}) + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", query, ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) } @@ -89,22 +101,26 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { headers := c.GetReqHeaders() {{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- - if value, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found { var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Too many values for ParamName {{.ParamName}}, 1 is required, but %d found", n)) + } {{if .IsPassThrough}} - params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{.GoName}} = valueList[0] {{end}} {{if .IsJson}} - err = json.Unmarshal([]byte(value), &{{.GoName}}) + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", value, &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) } @@ -113,20 +129,20 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} } {{if .Required}}else { - err = fmt.Errorf("Header parameter {{.ParamName}} is required, but not found: %w", err) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) + return fiber.NewError(fiber.StatusBadRequest, "Header parameter {{.ParamName}} is required, but not found") }{{end}} {{end}} {{end}} {{range .CookieParams}} - var cookie string + { + cookie := c.Cookies("{{.ParamName}}") - if cookie = c.Cookies("{{.ParamName}}"); cookie == "" { + if cookie != "" { {{- if .IsPassThrough}} - params.{{.GoName}} = {{if .HasOptionalPointer}}}&{{end}}cookie + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}cookie {{end}} {{- if .IsJson}} @@ -147,7 +163,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { {{- if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie, &value, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) } @@ -161,9 +177,22 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, err.Error()) } {{- end}} + } {{end}} {{end}} - return siw.Handler.{{.OperationId}}(c{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}}) + handler := func(c *fiber.Ctx) error { + return siw.Handler.{{.OperationId}}(c{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}}) + } + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + m := siw.HandlerMiddlewares[i] + next := handler + handler = func(c *fiber.Ctx) error { + return m(c, next) + } + } + + return handler(c) } -{{end}} +{{end}}{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-receiver.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-receiver.tmpl new file mode 100644 index 000000000..ea0b4e647 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/fiber/fiber-receiver.tmpl @@ -0,0 +1,99 @@ +// {{.Prefix}}ReceiverInterface represents handlers for receiving inbound +// {{.PrefixLower}} requests. Each {{.PrefixLower}} becomes a Handle*{{.Prefix}} +// method that the implementation fills in. The caller mounts the per- +// {{.PrefixLower}} fiber.Handler returned by {Op}{{.Prefix}}Handler at +// whatever URL path they advertise to senders. +type {{.Prefix}}ReceiverInterface interface { +{{range .Operations -}} +{{.SummaryAsComment ""}} +// Handle{{.OperationId}}{{$.Prefix}} handles the {{.Method}} {{$.PrefixLower}} for {{.SourceName}}. +Handle{{.OperationId}}{{$.Prefix}}(c *fiber.Ctx{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error +{{end}} +} + +{{range .Operations -}} +{{$opid := .OperationId -}} +{{$srcName := .SourceName -}} +// {{$opid}}{{$.Prefix}}Handler returns the fiber.Handler for the {{$srcName}} {{$.PrefixLower}}. +// Mount this at the URL path advertised to {{$.PrefixLower}} senders. Errors +// during parameter binding are returned via fiber.NewError so fiber's +// error chain reports them as 400. Per-handler middleware is not +// generated here; use fiber.App.Use() for engine-level middleware. +func {{$opid}}{{$.Prefix}}Handler(si {{$.Prefix}}ReceiverInterface) fiber.Handler { + return func(c *fiber.Ctx) error { +{{- if .RequiresParamObject}} + var err error + _ = err + + // Parameter object where we will unmarshal all parameters from the context. + var params {{$opid}}Params +{{if .QueryParams}} + var query url.Values + query, err = url.ParseQuery(string(c.Request().URI().QueryString())) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for query string: %w", err).Error()) + } +{{end}} +{{range $paramIdx, $param := .QueryParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", query, ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) + } + {{else}} + if paramValue := c.Query("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + return fiber.NewError(fiber.StatusBadRequest, "Query argument {{.ParamName}} is required, but not found") + }{{end}} + {{end}} +{{end}} +{{if .HeaderParams}} + headers := c.GetReqHeaders() +{{range $paramIdx, $param := .HeaderParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Too many values for header {{.ParamName}}, 1 is required, but %d found", n)) + } + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{end}} + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON: %w", err).Error()) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + }{{if .Required}} else { + return fiber.NewError(fiber.StatusBadRequest, "Header parameter {{.ParamName}} is required, but not found") + }{{end}} +{{end}} +{{end}} +{{- end}} + return si.Handle{{$opid}}{{$.Prefix}}(c{{if .RequiresParamObject}}, params{{end}}) + } +} + +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-interface.tmpl index 49a18f812..6a8a878f2 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-interface.tmpl @@ -1,7 +1,9 @@ // ServerInterface represents all server handlers. type ServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{.OperationId}}(c *gin.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) -{{end}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{.OperationId}}(c *gin.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}}{{end}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-receiver.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-receiver.tmpl new file mode 100644 index 000000000..db0faf78f --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-receiver.tmpl @@ -0,0 +1,97 @@ +// {{.Prefix}}ReceiverInterface represents handlers for receiving inbound +// {{.PrefixLower}} requests. Each {{.PrefixLower}} becomes a Handle*{{.Prefix}} +// method that the implementation fills in. The caller mounts the per- +// {{.PrefixLower}} gin.HandlerFunc returned by {Op}{{.Prefix}}Handler at +// whatever URL path they advertise to senders. +type {{.Prefix}}ReceiverInterface interface { +{{range .Operations -}} +{{.SummaryAsComment ""}} +// Handle{{.OperationId}}{{$.Prefix}} handles the {{.Method}} {{$.PrefixLower}} for {{.SourceName}}. +Handle{{.OperationId}}{{$.Prefix}}(c *gin.Context{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}} +} + +{{range .Operations -}} +{{$opid := .OperationId -}} +{{$srcName := .SourceName -}} +// {{$opid}}{{$.Prefix}}Handler returns the gin.HandlerFunc for the {{$srcName}} {{$.PrefixLower}}. +// Mount this at the URL path advertised to {{$.PrefixLower}} senders. +// Parameter-binding errors abort the request with 400 and a JSON body +// of the form {"error": "..."}. Engine-level middleware can be applied +// via gin.Engine.Use(); per-handler middleware is not generated here +// (gin's idiom prefers route-group / engine .Use composition). +func {{$opid}}{{$.Prefix}}Handler(si {{$.Prefix}}ReceiverInterface) gin.HandlerFunc { + return func(c *gin.Context) { +{{- if .RequiresParamObject}} + var err error + _ = err + + // Parameter object where we will unmarshal all parameters from the context. + var params {{$opid}}Params +{{range $paramIdx, $param := .QueryParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", c.Request.URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)}) + return + } + {{else}} + if paramValue := c.Query("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + c.JSON(http.StatusBadRequest, gin.H{"error": "Query parameter {{.ParamName}} is required, but not found"}) + return + }{{end}} + {{end}} +{{end}} +{{range $paramIdx, $param := .HeaderParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := c.Request.Header[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n)}) + return + } + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{end}} + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + }{{if .Required}} else { + c.JSON(http.StatusBadRequest, gin.H{"error": "Header parameter {{.ParamName}} is required, but not found"}) + return + }{{end}} +{{end}} +{{- end}} + si.Handle{{$opid}}{{$.Prefix}}(c{{if .RequiresParamObject}}, params{{end}}) + } +} + +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-register.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-register.tmpl index 8f03722ab..43b9ae250 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-register.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-register.tmpl @@ -28,6 +28,6 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options {{end}} {{range . -}} - router.{{.Method }}(options.BaseURL+"{{.Path | swaggerUriToGinUri }}", wrapper.{{.OperationId}}) + router.{{.Method }}(options.BaseURL+{{.Path | swaggerUriToGinUri | toGoString}}, wrapper.{{.HandlerName}}) {{end -}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-wrappers.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-wrappers.tmpl index 3bc02e5d7..211b13dc8 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-wrappers.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gin/gin-wrappers.tmpl @@ -8,29 +8,30 @@ type ServerInterfaceWrapper struct { type MiddlewareFunc func(c *gin.Context) {{range .}}{{$opid := .OperationId}} - +{{if not .IsAlias}} // {{$opid}} operation middleware func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { {{if or .RequiresParamObject (gt (len .PathParams) 0) }} var err error + _ = err {{end}} {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" ------------- var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}} {{if .IsPassThrough}} - {{$varName}} = c.Query("{{.ParamName}}") + {{$varName}} = c.Param("{{.ParamName}}") {{end}} {{if .IsJson}} - err = json.Unmarshal([]byte(c.Query("{{.ParamName}}")), &{{$varName}}) + err = json.Unmarshal([]byte(c.Param("{{.ParamName}}")), &{{$varName}}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Error unmarshaling parameter '{{.ParamName}}' as JSON"), http.StatusBadRequest) return } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", c.Param("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", c.Param("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}", ValueIsUnescaped: true}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err), http.StatusBadRequest) return @@ -39,9 +40,11 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { {{end}} +{{if opts.Compatibility.EnableAuthScopesOnContext -}} {{range .SecurityDefinitions}} - c.Set({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes, {{toStringArray .Scopes}}) + c.Set(string({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes), {{toStringArray .Scopes}}) {{end}} +{{- end}} {{if .RequiresParamObject}} // Parameter object where we will unmarshal all parameters from the context @@ -51,7 +54,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}} // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- {{ end }} - {{ if (or (or .Required .IsPassThrough) .IsJson) }} + {{ if (or .IsPassThrough .IsJson) }} if paramValue := c.Query("{{.ParamName}}"); paramValue != "" { {{if .IsPassThrough}} @@ -75,7 +78,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { {{end}} {{if .IsStyled}} - err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", c.Request.URL.Query(), ¶ms.{{.GoName}}) + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", c.Request.URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err), http.StatusBadRequest) return @@ -96,7 +99,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { } {{if .IsPassThrough}} - params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{.GoName}} = valueList[0] {{end}} {{if .IsJson}} @@ -108,7 +111,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err), http.StatusBadRequest) return @@ -155,7 +158,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { {{- if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie, &value, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err), http.StatusBadRequest) return @@ -183,4 +186,4 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { siw.Handler.{{.OperationId}}(c{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}}) } -{{end}} +{{end}}{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-interface.tmpl index 79a51fd75..d3936346d 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-interface.tmpl @@ -1,7 +1,9 @@ // ServerInterface represents all server handlers. type ServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) -{{end}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}}{{end}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl index e8aa9799f..8b5f54054 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl @@ -8,11 +8,12 @@ type ServerInterfaceWrapper struct { type MiddlewareFunc func(http.Handler) http.Handler {{range .}}{{$opid := .OperationId}} - +{{if not .IsAlias}} // {{$opid}} operation middleware func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Request) { {{if or .RequiresParamObject (gt (len .PathParams) 0) }} var err error + _ = err {{end}} {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" ------------- @@ -29,7 +30,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", mux.Vars(r)["{{.ParamName}}"], &{{$varName}}, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", mux.Vars(r)["{{.ParamName}}"], &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}", ValueIsUnescaped: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -38,7 +39,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{end}} - {{if .SecurityDefinitions -}} + {{if and .SecurityDefinitions opts.Compatibility.EnableAuthScopesOnContext -}} ctx := r.Context() {{range .SecurityDefinitions}} ctx = context.WithValue(ctx, {{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes, {{toStringArray .Scopes}}) @@ -54,7 +55,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}} // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- {{ end }} - {{ if (or (or .Required .IsPassThrough) .IsJson) }} + {{ if (or .IsPassThrough .IsJson) }} if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" { {{if .IsPassThrough}} @@ -77,9 +78,14 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ }{{end}} {{end}} {{if .IsStyled}} - err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), ¶ms.{{.GoName}}) + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"}) + } else { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + } return } {{end}} @@ -98,7 +104,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ } {{if .IsPassThrough}} - params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{.GoName}} = valueList[0] {{end}} {{if .IsJson}} @@ -110,7 +116,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -159,7 +165,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{- if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -194,7 +200,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ handler.ServeHTTP(w, r) } -{{end}} +{{end}}{{end}} type UnescapedCookieParamError struct { ParamName string diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-receiver.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-receiver.tmpl new file mode 100644 index 000000000..3089faea4 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-receiver.tmpl @@ -0,0 +1,116 @@ +// {{.Prefix}}ReceiverInterface represents handlers for receiving inbound +// {{.PrefixLower}} requests. Each {{.PrefixLower}} becomes a Handle*{{.Prefix}} +// method that the implementation fills in. The caller mounts the per- +// {{.PrefixLower}} http.Handler returned by {Op}{{.Prefix}}Handler at +// whatever URL path they advertise to senders. +type {{.Prefix}}ReceiverInterface interface { +{{range .Operations -}} +{{.SummaryAsComment ""}} +// Handle{{.OperationId}}{{$.Prefix}} handles the {{.Method}} {{$.PrefixLower}} for {{.SourceName}}. +Handle{{.OperationId}}{{$.Prefix}}(w http.ResponseWriter, r *http.Request{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}} +} + +// {{.Prefix}}ReceiverMiddlewareFunc wraps an http.Handler with cross- +// cutting behavior (signature verification, logging, rate limiting, ...). +type {{.Prefix}}ReceiverMiddlewareFunc func(http.Handler) http.Handler + +{{range .Operations -}} +{{$opid := .OperationId -}} +{{$srcName := .SourceName -}} +// {{$opid}}{{$.Prefix}}Handler returns the http.Handler for the {{$srcName}} {{$.PrefixLower}}. +// Mount this at the URL path advertised to {{$.PrefixLower}} senders. errHandler +// may be nil; if so, parameter-binding errors return 400 with the error +// message. Middlewares are applied in the order provided -- the last +// argument becomes the outermost wrapper. +func {{$opid}}{{$.Prefix}}Handler(si {{$.Prefix}}ReceiverInterface, errHandler func(w http.ResponseWriter, r *http.Request, err error), middlewares ...{{$.Prefix}}ReceiverMiddlewareFunc) http.Handler { + if errHandler == nil { + errHandler = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +{{- if .RequiresParamObject}} + var err error + _ = err + + // Parameter object where we will unmarshal all parameters from the request. + var params {{$opid}}Params +{{range $paramIdx, $param := .QueryParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{if (or .IsPassThrough .IsJson)}} + if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + errHandler(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + errHandler(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"}) + return + }{{end}} + {{end}} + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + errHandler(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"}) + } else { + errHandler(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + } + return + } + {{end}} +{{end}} +{{range $paramIdx, $param := .HeaderParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := r.Header[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + errHandler(w, r, &TooManyValuesForParamError{ParamName: "{{.ParamName}}", Count: n}) + return + } + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{end}} + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + errHandler(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + errHandler(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + }{{if .Required}} else { + err := fmt.Errorf("Header parameter {{.ParamName}} is required, but not found") + errHandler(w, r, &RequiredHeaderError{ParamName: "{{.ParamName}}", Err: err}) + return + }{{end}} +{{end}} +{{- end}} + si.Handle{{$opid}}{{$.Prefix}}(w, r{{if .RequiresParamObject}}, params{{end}}) + }) + for _, mw := range middlewares { + h = mw(h) + } + return h +} + +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-register.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-register.tmpl index b019e1dbb..853ab32c3 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-register.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/gorilla/gorilla-register.tmpl @@ -43,7 +43,7 @@ ErrorHandlerFunc: options.ErrorHandlerFunc, } {{end}} {{range .}} -r.HandleFunc(options.BaseURL+"{{.Path | swaggerUriToGorillaUri }}", wrapper.{{.OperationId}}).Methods("{{.Method }}") +r.HandleFunc(options.BaseURL+{{.Path | swaggerUriToGorillaUri | toGoString}}, wrapper.{{.HandlerName}}).Methods({{.Method | httpMethodConstant}}) {{end}} return r } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/imports.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/imports.tmpl index 4d1942242..d57c2d1cd 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/imports.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/imports.tmpl @@ -8,14 +8,14 @@ package {{.PackageName}} import ( "bytes" - "compress/gzip" + "compress/flate" "context" "encoding/base64" "encoding/json" "encoding/xml" "errors" "fmt" - "gopkg.in/yaml.v2" + "go.yaml.in/yaml/v3" "io" "os" "mime" @@ -28,19 +28,11 @@ import ( "github.com/oapi-codegen/runtime" "github.com/oapi-codegen/nullable" - strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo" - strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin" - strictiris "github.com/oapi-codegen/runtime/strictmiddleware/iris" - strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" openapi_types "github.com/oapi-codegen/runtime/types" "github.com/getkin/kin-openapi/openapi3" - "github.com/go-chi/chi/v5" - "github.com/labstack/echo/v4" - "github.com/gin-gonic/gin" - "github.com/gofiber/fiber/v2" - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/core/router" - "github.com/gorilla/mux" + {{- range .RouterImports}} + {{if .Alias}}{{.Alias}} {{end}}"{{.Package}}" + {{- end}} {{- range .ExternalImports}} {{ . }} {{- end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/inline.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/inline.tmpl index de0012050..b76ec3336 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/inline.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/inline.tmpl @@ -1,24 +1,26 @@ -// Base64 encoded, gzipped, json marshaled Swagger object +// Base64 encoded, compressed with deflate, json marshaled OpenAPI spec. +// Stored as a slice of fixed-width chunks rather than one concatenated +// const string: with thousands of chunks the chained `+` fold is several +// times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ -{{range .SpecParts}} - "{{.}}",{{end}} -} +{{range .SpecParts}} "{{.}}", +{{end}}} -// GetSwagger returns the content of the embedded swagger specification file -// or error if failed to decode +// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, +// after base64-decoding and flate-decompressing the embedded blob. func decodeSpec() ([]byte, error) { - zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + encoded := strings.Join(swaggerSpec, "") + compressed, err := base64.StdEncoding.DecodeString(encoded) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %w", err) } - zr, err := gzip.NewReader(bytes.NewReader(zipped)) - if err != nil { - return nil, fmt.Errorf("error decompressing spec: %w", err) - } + zr := flate.NewReader(bytes.NewReader(compressed)) var buf bytes.Buffer - _, err = buf.ReadFrom(zr) - if err != nil { - return nil, fmt.Errorf("error decompressing spec: %w", err) + if _, err := buf.ReadFrom(zr); err != nil { + return nil, fmt.Errorf("read flate: %w", err) + } + if err := zr.Close(); err != nil { + return nil, fmt.Errorf("close flate reader: %w", err) } return buf.Bytes(), nil @@ -26,7 +28,7 @@ func decodeSpec() ([]byte, error) { var rawSpec = decodeSpecCached() -// a naive cached of a decoded swagger spec +// a naive cache of the decoded OpenAPI spec func decodeSpecCached() func() ([]byte, error) { data, err := decodeSpec() return func() ([]byte, error) { @@ -51,12 +53,12 @@ func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { return res } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. The external references of Swagger specification are resolved. -// The logic of resolving external references is tightly connected to "import-mapping" feature. -// Externally referenced files must be embedded in the corresponding golang packages. -// Urls can be supported but this task was out of the scope. -func GetSwagger() (swagger *openapi3.T, err error) { +// GetSpec returns the OpenAPI specification corresponding to the generated +// code in this file. External references in the spec are resolved through +// PathToRawSpec; externally-referenced files must be embedded in their +// corresponding Go packages (via the import-mapping feature). URL-based +// external refs are not supported. +func GetSpec() (swagger *openapi3.T, err error) { resolvePath := PathToRawSpec("") loader := openapi3.NewLoader() @@ -82,3 +84,22 @@ func GetSwagger() (swagger *openapi3.T, err error) { } return } + +// GetSpecJSON returns the raw JSON bytes of the embedded OpenAPI +// specification: decompressed but not unmarshaled. External references +// are not resolved here; the bytes are the spec exactly as embedded by +// codegen. The result is cached at package init time, so repeated calls +// are cheap. +func GetSpecJSON() ([]byte, error) { + return rawSpec() +} + +// GetSwagger returns the OpenAPI specification corresponding to the +// generated code in this file. +// +// Deprecated: GetSwagger predates kin-openapi renaming openapi3.Swagger +// to openapi3.T. Use [GetSpec] instead. This wrapper is retained for +// backwards compatibility. +func GetSwagger() (*openapi3.T, error) { + return GetSpec() +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-handler.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-handler.tmpl index 3f1228fae..795cf4f10 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-handler.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-handler.tmpl @@ -12,12 +12,16 @@ func RegisterHandlers(router *iris.Application, si ServerInterface) { // RegisterHandlersWithOptions creates http.Handler with additional options func RegisterHandlersWithOptions(router *iris.Application, si ServerInterface, options IrisServerOptions) { + for _, m := range options.Middlewares { + router.Use(m) + } + {{if .}} wrapper := ServerInterfaceWrapper{ Handler: si, } {{end}} -{{range .}}router.{{.Method | lower | title}}(options.BaseURL + "{{.Path | swaggerUriToIrisUri}}", wrapper.{{.OperationId}}) +{{range .}}router.{{.Method | lower | title}}(options.BaseURL + {{.Path | swaggerUriToIrisUri | toGoString}}, wrapper.{{.HandlerName}}) {{end}} router.Build() } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-interface.tmpl index fb53a124b..f9f949732 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-interface.tmpl @@ -1,7 +1,9 @@ // ServerInterface represents all server handlers. type ServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{.OperationId}}(ctx iris.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) -{{end}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{.OperationId}}(ctx iris.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}}{{end}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-middleware.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-middleware.tmpl index 814e6bc4e..41debe3d9 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-middleware.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-middleware.tmpl @@ -5,19 +5,20 @@ type ServerInterfaceWrapper struct { type MiddlewareFunc iris.Handler -{{range .}}{{$opid := .OperationId}}// {{$opid}} converts iris context to params. +{{range .}}{{$opid := .OperationId}}{{if not .IsAlias}}// {{$opid}} converts iris context to params. func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { {{if or .RequiresParamObject (gt (len .PathParams) 0) }} var err error + _ = err {{end}} {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" ------------- var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}} {{if .IsPassThrough}} - {{$varName}} = ctx.URLParam("{{.ParamName}}") + {{$varName}} = ctx.Params().Get("{{.ParamName}}") {{end}} {{if .IsJson}} - err = json.Unmarshal([]byte(ctx.URLParam("{{.ParamName}}")), &{{$varName}}) + err = json.Unmarshal([]byte(ctx.Params().Get("{{.ParamName}}")), &{{$varName}}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.WriteString("Error unmarshaling parameter '{{.ParamName}}' as JSON") @@ -25,7 +26,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", ctx.Params().Get("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", ctx.Params().Get("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}", ValueIsUnescaped: true}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err) @@ -34,9 +35,11 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { {{end}} {{end}} +{{if opts.Compatibility.EnableAuthScopesOnContext -}} {{range .SecurityDefinitions}} - ctx.Set({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes, {{toStringArray .Scopes}}) + ctx.Set(string({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes), {{toStringArray .Scopes}}) {{end}} +{{- end}} {{if .RequiresParamObject}} // Parameter object where we will unmarshal all parameters from the context @@ -46,14 +49,14 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- {{ end }} {{if .IsStyled}} - err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.Request().URL.Query(), ¶ms.{{.GoName}}) + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.Request().URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err) return } {{else}} - if paramValue := ctx.QueryParam("{{.ParamName}}"); paramValue != "" { + if paramValue := ctx.URLParam("{{.ParamName}}"); paramValue != "" { {{if .IsPassThrough}} params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue {{end}} @@ -87,7 +90,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { return } {{if .IsPassThrough}} - params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{.GoName}} = valueList[0] {{end}} {{if .IsJson}} err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) @@ -98,7 +101,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err) @@ -115,14 +118,14 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { {{end}} {{range .CookieParams}} - if cookie, err := ctx.Cookie("{{.ParamName}}"); err == nil { + if cookie := ctx.GetCookie("{{.ParamName}}"); cookie != "" { {{if .IsPassThrough}} - params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}cookie.Value + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}cookie {{end}} {{if .IsJson}} var value {{.TypeDef}} var decoded string - decoded, err := url.QueryUnescape(cookie.Value) + decoded, err := url.QueryUnescape(cookie) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.WriteString("Error unescaping cookie parameter '{{.ParamName}}'") @@ -138,7 +141,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { {{end}} {{if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err) @@ -158,4 +161,4 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { // Invoke the callback with all the unmarshaled arguments w.Handler.{{.OperationId}}(ctx{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}}) } -{{end}} +{{end}}{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-receiver.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-receiver.tmpl new file mode 100644 index 000000000..324d98c42 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/iris/iris-receiver.tmpl @@ -0,0 +1,103 @@ +// {{.Prefix}}ReceiverInterface represents handlers for receiving inbound +// {{.PrefixLower}} requests. Each {{.PrefixLower}} becomes a Handle*{{.Prefix}} +// method that the implementation fills in. The caller mounts the per- +// {{.PrefixLower}} iris.Handler returned by {Op}{{.Prefix}}Handler at +// whatever URL path they advertise to senders. +type {{.Prefix}}ReceiverInterface interface { +{{range .Operations -}} +{{.SummaryAsComment ""}} +// Handle{{.OperationId}}{{$.Prefix}} handles the {{.Method}} {{$.PrefixLower}} for {{.SourceName}}. +Handle{{.OperationId}}{{$.Prefix}}(ctx iris.Context{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}} +} + +{{range .Operations -}} +{{$opid := .OperationId -}} +{{$srcName := .SourceName -}} +// {{$opid}}{{$.Prefix}}Handler returns the iris.Handler for the {{$srcName}} {{$.PrefixLower}}. +// Mount this at the URL path advertised to {{$.PrefixLower}} senders. +// Parameter-binding errors set status 400 with a plain-text reason. +// Per-handler middleware is not generated; iris's idiom prefers +// app.Use() / Party-level middleware. +func {{$opid}}{{$.Prefix}}Handler(si {{$.Prefix}}ReceiverInterface) iris.Handler { + return func(ctx iris.Context) { +{{- if .RequiresParamObject}} + var err error + _ = err + + // Parameter object where we will unmarshal all parameters from the context. + var params {{$opid}}Params +{{range $paramIdx, $param := .QueryParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.Request().URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + ctx.StatusCode(http.StatusBadRequest) + _, _ = ctx.WriteString(fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + return + } + {{else}} + if paramValue := ctx.URLParam("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + ctx.StatusCode(http.StatusBadRequest) + _, _ = ctx.WriteString(fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + ctx.StatusCode(http.StatusBadRequest) + _, _ = ctx.WriteString("Query parameter {{.ParamName}} is required, but not found") + return + }{{end}} + {{end}} +{{end}} +{{range $paramIdx, $param := .HeaderParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := ctx.Request().Header[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + ctx.StatusCode(http.StatusBadRequest) + _, _ = ctx.WriteString(fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n)) + return + } + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{end}} + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + ctx.StatusCode(http.StatusBadRequest) + _, _ = ctx.WriteString(fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + ctx.StatusCode(http.StatusBadRequest) + _, _ = ctx.WriteString(fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + }{{if .Required}} else { + ctx.StatusCode(http.StatusBadRequest) + _, _ = ctx.WriteString("Header parameter {{.ParamName}} is required, but not found") + return + }{{end}} +{{end}} +{{- end}} + si.Handle{{$opid}}{{$.Prefix}}(ctx{{if .RequiresParamObject}}, params{{end}}) + } +} + +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/param-types.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/param-types.tmpl index bb107b8f0..bccf29102 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/param-types.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/param-types.tmpl @@ -1,6 +1,6 @@ {{range .}}{{$opid := .OperationId}} {{range .TypeDefinitions}} -// {{.TypeName}} defines parameters for {{$opid}}. +{{if .Comment}}{{.Comment}}{{else}}// {{.TypeName}} defines parameters for {{$opid}}.{{end}} type {{.TypeName}} {{if .IsAlias}}={{end}} {{.Schema.TypeDecl}} {{end}} {{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/request-bodies.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/request-bodies.tmpl index 9661691a1..46415191e 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/request-bodies.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/request-bodies.tmpl @@ -4,6 +4,10 @@ {{$contentType := .ContentType -}} {{with .TypeDef $opid}} // {{.TypeName}} defines body for {{$opid}} for {{$contentType}} ContentType. +{{- with .DeprecationComment}} +// +{{.}} +{{- end}} type {{.TypeName}} {{if .IsAlias}}={{end}} {{.Schema.TypeDecl}} {{end}} {{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/server-urls.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/server-urls.tmpl index f3599e5fa..b0cf2a86a 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/server-urls.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/server-urls.tmpl @@ -1,54 +1,79 @@ {{ range . }} -{{ if eq 0 (len .OAPISchema.Variables) }} -{{/* URLs without variables are straightforward, so we'll create them a constant */}} -// {{ .GoName }} defines the Server URL for {{ .OAPISchema.Description }} -const {{ .GoName}} = "{{ .OAPISchema.URL }}" +{{ $usedVars := .UsedVariables }} +{{ $undeclared := .UndeclaredPlaceholders }} +{{ if and (eq 0 (len $usedVars)) (eq 0 (len $undeclared)) }} +{{/* URLs without variables (declared or used) are straightforward, so we'll create them a constant */}} +// {{ .GoName }} defines the Server URL for {{ if len .OAPISchema.Description }}{{ stripNewLines .OAPISchema.Description }}{{ else }}{{ stripNewLines .OAPISchema.URL }}{{ end }} +const {{ .GoName}} = {{ .OAPISchema.URL | toGoString }} {{ else }} {{/* URLs with variables are not straightforward, as we may need multiple types, and so will model them as a function */}} -{{/* first, we'll start by generating requisite types */}} - {{ $goName := .GoName }} -{{ range $k, $v := .OAPISchema.Variables }} + +{{/* + For each USED variable, emit any inline declarations server-urls.tmpl + is still responsible for. Variables declared but not referenced by + a `{name}` placeholder in the URL are filtered out earlier + (https://github.com/oapi-codegen/oapi-codegen/issues/2004) so we + don't generate types/consts/params that the function would never + use. + + * Variables with `enum` get their `type` declaration and `const` + block (and a `Valid()` method) from typedef.tmpl + constants.tmpl + via GenerateEnums; here we only emit the optional default-pointer + constant — and we name it `…VariableDefaultValue` so it can never + collide with an enum value whose Go identifier is `Default` (e.g. + OpenAPI `enum: [default]`). + + * Variables without `enum` need their `type Foo string` and any + `const FooDefault = "…"` emitted here, since the generic enum + path has nothing to contribute for a non-enum string. +*/}} +{{ range $k, $v := $usedVars }} {{ $prefix := printf "%s%sVariable" $goName ($k | ucFirst) }} + {{ if eq (len $v.Enum) 0 }} // {{ $prefix }} is the `{{ $k }}` variable for {{ $goName }} type {{ $prefix }} string - {{ range $v.Enum }} - {{/* TODO this may result in broken generated code if any of the `enum` values are the literal value `default` https://github.com/oapi-codegen/oapi-codegen/issues/2003 */}} - // {{ $prefix }}{{ . | ucFirst }} is one of the accepted values for the `{{ $k }}` variable for {{ $goName }} - const {{ $prefix }}{{ . | ucFirst }} {{ $prefix }} = "{{ . }}" - {{ end }} - - {{/* TODO we should introduce a `Valid() error` method to enums https://github.com/oapi-codegen/oapi-codegen/issues/2006 */}} - {{ if $v.Default }} - {{ if gt (len $v.Enum) 0 }} - {{/* if we have an enum, we should use the type defined for it for its default value - and reference the constant we've already defined for the value */}} - {{/* TODO this may result in broken generated code if any of the `enum` values are the literal value `default` https://github.com/oapi-codegen/oapi-codegen/issues/2003 */}} - {{/* TODO this may result in broken generated code if the `default` isn't found in `enum` (which is an issue with the spec) https://github.com/oapi-codegen/oapi-codegen/issues/2007 */}} - // {{ $prefix }}Default is the default choice, for the accepted values for the `{{ $k }}` variable for {{ $goName }} - const {{ $prefix }}Default {{ $prefix }} = {{ $prefix }}{{ $v.Default | ucFirst }} - {{ else }} - // {{ $prefix }}Default is the default value for the `{{ $k }}` variable for {{ $goName }} - const {{ $prefix }}Default = "{{ $v.Default }}" - {{ end }} + // {{ $prefix }}Default is the default value for the `{{ $k }}` variable for {{ $goName }} + const {{ $prefix }}Default = {{ $v.Default | toGoString }} + {{ end }} {{ end }} {{ end }} +{{/* + Enum-typed default-pointer constants. Their identifier names are + pre-computed in Go (ServerObjectDefinition.EnumDefaultPointers) so + that the pointer's reference to the enum-value constant always + matches the post-dedup name actually emitted by constants.tmpl, + and the pointer itself is renamed `…DefaultValue` only for specs + that would have collided under the old codegen anyway. +*/}} +{{ range .EnumDefaultPointers }} + // {{ .PointerName }} is the default choice, for the accepted values for the `{{ .VariableName }}` variable + const {{ .PointerName }} {{ .TypeName }} = {{ .TargetName }} +{{ end }} -// New{{ .GoName }} constructs the Server URL for {{ .OAPISchema.Description }}, with the provided variables. -func New{{ .GoName }}({{ genServerURLWithVariablesFunctionParams .GoName .OAPISchema.Variables }}) (string, error) { - u := "{{ .OAPISchema.URL }}" - {{ range $k, $v := .OAPISchema.Variables }} - {{- $placeholder := printf "{%s}" $k -}} +// New{{ .GoName }} constructs the Server URL for {{ stripNewLines .OAPISchema.Description }}, with the provided variables. +func New{{ .GoName }}({{ .NewServerFunctionParams }}) (string, error) { + {{ range $k, $v := $usedVars }} {{- if gt (len $v.Enum) 0 -}} - {{/* TODO https://github.com/oapi-codegen/oapi-codegen/issues/2006 */}} - // TODO in the future, this will validate that the value is part of the {{ printf "%s%sVariable" $goName ($k | ucFirst) }} enum + if !{{ $k }}.Valid() { + return "", fmt.Errorf("`%v` is not one of the accepted values for the `{{ $k }}` variable", {{ $k }}) + } {{ end -}} + {{ end }} + u := {{ .OAPISchema.URL | toGoString }} + + {{ range $k, $v := $usedVars }} + {{- $placeholder := printf "{%s}" $k -}} u = strings.ReplaceAll(u, "{{ $placeholder }}", string({{ $k }})) {{ end }} + {{ range $k := $undeclared }} + {{- $placeholder := printf "{%s}" $k -}} + u = strings.ReplaceAll(u, "{{ $placeholder }}", {{ $k }}) + {{ end }} if strings.Contains(u, "{") || strings.Contains(u, "}") { return "", fmt.Errorf("after mapping variables, there were still `{` or `}` characters in the string: %#v", u) diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-handler.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-handler.tmpl index bed4685c7..676748599 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-handler.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-handler.tmpl @@ -3,10 +3,10 @@ func Handler(si ServerInterface) http.Handler { return HandlerWithOptions(si, StdHTTPServerOptions{}) } -// ServeMux is an abstraction of http.ServeMux. +// ServeMux is an abstraction of [http.ServeMux]. type ServeMux interface { HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) - ServeHTTP(w http.ResponseWriter, r *http.Request) + http.Handler } type StdHTTPServerOptions struct { @@ -49,7 +49,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H ErrorHandlerFunc: options.ErrorHandlerFunc, } {{end}} -{{range .}}m.HandleFunc("{{.Method }} "+options.BaseURL+"{{.Path | swaggerUriToStdHttpUri}}", wrapper.{{.OperationId}}) +{{range .}}m.HandleFunc({{.Method | httpMethodConstant}}+" "+options.BaseURL+{{.Path | swaggerUriToStdHttpUri | toGoString}}, wrapper.{{.HandlerName}}) {{end}} return m } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-interface.tmpl index 79a51fd75..d3936346d 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-interface.tmpl @@ -1,7 +1,9 @@ // ServerInterface represents all server handlers. type ServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) -{{end}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}}{{end}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-middleware.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-middleware.tmpl index 09977358c..5998dbb61 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-middleware.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-middleware.tmpl @@ -8,28 +8,29 @@ type ServerInterfaceWrapper struct { type MiddlewareFunc func(http.Handler) http.Handler {{range .}}{{$opid := .OperationId}} - +{{if not .IsAlias}} // {{$opid}} operation middleware func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Request) { {{if or .RequiresParamObject (gt (len .PathParams) 0) }} var err error + _ = err {{end}} {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" ------------- var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}} {{if .IsPassThrough}} - {{$varName}} = r.PathValue("{{.ParamName}}") + {{$varName}} = r.PathValue("{{.SanitizedParamName}}") {{end}} {{if .IsJson}} - err = json.Unmarshal([]byte(r.PathValue("{{.ParamName}}")), &{{$varName}}) + err = json.Unmarshal([]byte(r.PathValue("{{.SanitizedParamName}}")), &{{$varName}}) if err != nil { siw.ErrorHandlerFunc(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err}) return } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", r.PathValue("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", r.PathValue("{{.SanitizedParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}", ValueIsUnescaped: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -38,7 +39,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{end}} - {{if .SecurityDefinitions -}} + {{if and .SecurityDefinitions opts.Compatibility.EnableAuthScopesOnContext -}} ctx := r.Context() {{range .SecurityDefinitions}} ctx = context.WithValue(ctx, {{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes, {{toStringArray .Scopes}}) @@ -54,7 +55,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}} // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- {{ end }} - {{ if (or (or .Required .IsPassThrough) .IsJson) }} + {{ if (or .IsPassThrough .IsJson) }} if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" { {{if .IsPassThrough}} @@ -77,9 +78,14 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ }{{end}} {{end}} {{if .IsStyled}} - err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), ¶ms.{{.GoName}}) + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"}) + } else { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + } return } {{end}} @@ -98,7 +104,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ } {{if .IsPassThrough}} - params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{.GoName}} = valueList[0] {{end}} {{if .IsJson}} @@ -110,7 +116,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -159,7 +165,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{- if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -194,7 +200,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ handler.ServeHTTP(w, r) } -{{end}} +{{end}}{{end}} type UnescapedCookieParamError struct { ParamName string diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-receiver.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-receiver.tmpl new file mode 100644 index 000000000..3089faea4 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/stdhttp/std-http-receiver.tmpl @@ -0,0 +1,116 @@ +// {{.Prefix}}ReceiverInterface represents handlers for receiving inbound +// {{.PrefixLower}} requests. Each {{.PrefixLower}} becomes a Handle*{{.Prefix}} +// method that the implementation fills in. The caller mounts the per- +// {{.PrefixLower}} http.Handler returned by {Op}{{.Prefix}}Handler at +// whatever URL path they advertise to senders. +type {{.Prefix}}ReceiverInterface interface { +{{range .Operations -}} +{{.SummaryAsComment ""}} +// Handle{{.OperationId}}{{$.Prefix}} handles the {{.Method}} {{$.PrefixLower}} for {{.SourceName}}. +Handle{{.OperationId}}{{$.Prefix}}(w http.ResponseWriter, r *http.Request{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) +{{end}} +} + +// {{.Prefix}}ReceiverMiddlewareFunc wraps an http.Handler with cross- +// cutting behavior (signature verification, logging, rate limiting, ...). +type {{.Prefix}}ReceiverMiddlewareFunc func(http.Handler) http.Handler + +{{range .Operations -}} +{{$opid := .OperationId -}} +{{$srcName := .SourceName -}} +// {{$opid}}{{$.Prefix}}Handler returns the http.Handler for the {{$srcName}} {{$.PrefixLower}}. +// Mount this at the URL path advertised to {{$.PrefixLower}} senders. errHandler +// may be nil; if so, parameter-binding errors return 400 with the error +// message. Middlewares are applied in the order provided -- the last +// argument becomes the outermost wrapper. +func {{$opid}}{{$.Prefix}}Handler(si {{$.Prefix}}ReceiverInterface, errHandler func(w http.ResponseWriter, r *http.Request, err error), middlewares ...{{$.Prefix}}ReceiverMiddlewareFunc) http.Handler { + if errHandler == nil { + errHandler = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +{{- if .RequiresParamObject}} + var err error + _ = err + + // Parameter object where we will unmarshal all parameters from the request. + var params {{$opid}}Params +{{range $paramIdx, $param := .QueryParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" ------------- + {{if (or .IsPassThrough .IsJson)}} + if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" { + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}paramValue + {{end}} + {{if .IsJson}} + var value {{.TypeDef}} + err = json.Unmarshal([]byte(paramValue), &value) + if err != nil { + errHandler(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}value + {{end}} + }{{if .Required}} else { + errHandler(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"}) + return + }{{end}} + {{end}} + {{if .IsStyled}} + err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + errHandler(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"}) + } else { + errHandler(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + } + return + } + {{end}} +{{end}} +{{range $paramIdx, $param := .HeaderParams}} + // ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" ------------- + if valueList, found := r.Header[http.CanonicalHeaderKey("{{.ParamName}}")]; found { + var {{.GoName}} {{.TypeDef}} + n := len(valueList) + if n != 1 { + errHandler(w, r, &TooManyValuesForParamError{ParamName: "{{.ParamName}}", Count: n}) + return + } + {{if .IsPassThrough}} + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}valueList[0] + {{end}} + {{if .IsJson}} + err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}}) + if err != nil { + errHandler(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + {{if .IsStyled}} + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + errHandler(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) + return + } + params.{{.GoName}} = {{if .HasOptionalPointer}}&{{end}}{{.GoName}} + {{end}} + }{{if .Required}} else { + err := fmt.Errorf("Header parameter {{.ParamName}} is required, but not found") + errHandler(w, r, &RequiredHeaderError{ParamName: "{{.ParamName}}", Err: err}) + return + }{{end}} +{{end}} +{{- end}} + si.Handle{{$opid}}{{$.Prefix}}(w, r{{if .RequiresParamObject}}, params{{end}}) + }) + for _, mw := range middlewares { + h = mw(h) + } + return h +} + +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-echo.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-echo.tmpl index 676690fab..404c0b726 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-echo.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-echo.tmpl @@ -1,5 +1,5 @@ -type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc -type StrictMiddlewareFunc = strictecho.StrictEchoMiddlewareFunc +type StrictHandlerFunc func(ctx echo.Context, request any) (any, error) +type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { return &strictHandler{ssi: ssi, middlewares: middlewares} @@ -12,6 +12,7 @@ type strictHandler struct { {{range .}} {{$opid := .OperationId}} + {{if not .IsAlias}} // {{$opid}} operation middleware func (sh *strictHandler) {{.OperationId}}(ctx echo.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error { var request {{$opid | ucFirst}}RequestObject @@ -30,13 +31,30 @@ type strictHandler struct { {{$multipleBodies := gt (len .Bodies) 1 -}} {{range .Bodies -}} - {{if $multipleBodies}}if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), "{{.ContentType}}") { {{end}} + {{if $multipleBodies}}if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), {{.ContentType | toGoString}}) { {{end}} {{if .IsJSON -}} var body {{$opid}}{{.NameTag}}RequestBody - if err := ctx.Bind(&body); err != nil { - return err + var err error + if binder, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = binder.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) } + if err != nil { + {{if not .Required -}} + if !errors.Is(err, io.EOF) { + return err + } + {{else -}} + return err + {{end -}} + } {{if not .Required -}} else { {{end}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} } {{end}} {{else if eq .NameTag "Formdata" -}} if form, err := ctx.FormParams(); err == nil { var body {{$opid}}{{.NameTag}}RequestBody @@ -68,8 +86,14 @@ type strictHandler struct { if err != nil { return err } + {{if not .Required -}} + if len(data) > 0 { + {{end -}} body := {{$opid}}{{.NameTag}}RequestBody(data) request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} + } + {{end -}} {{else -}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = ctx.Request().Body {{end}}{{/* if eq .NameTag "JSON" */ -}} @@ -94,4 +118,5 @@ type strictHandler struct { } return nil } + {{end}} {{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-echo5.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-echo5.tmpl new file mode 100644 index 000000000..551759c9e --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-echo5.tmpl @@ -0,0 +1,116 @@ +type StrictHandlerFunc func(ctx *echo.Context, request any) (any, error) +type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +{{range .}} + {{$opid := .OperationId}} + {{if not .IsAlias}} + // {{$opid}} operation middleware + func (sh *strictHandler) {{.OperationId}}(ctx *echo.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error { + var request {{$opid | ucFirst}}RequestObject + + {{range .PathParams -}} + request.{{.GoName}} = {{.GoVariableName}} + {{end -}} + + {{if .RequiresParamObject -}} + request.Params = params + {{end -}} + + {{ if .HasMaskedRequestContentTypes -}} + request.ContentType = ctx.Request().Header.Get("Content-Type") + {{end -}} + + {{$multipleBodies := gt (len .Bodies) 1 -}} + {{range .Bodies -}} + {{if $multipleBodies}}if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), {{.ContentType | toGoString}}) { {{end}} + {{if .IsJSON -}} + var body {{$opid}}{{.NameTag}}RequestBody + var err error + if _, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = echo.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { + {{if not .Required -}} + if !errors.Is(err, io.EOF) { + return err + } + {{else -}} + return err + {{end -}} + } {{if not .Required -}} else { {{end}} + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} } {{end}} + {{else if eq .NameTag "Formdata" -}} + if form, err := ctx.FormValues(); err == nil { + var body {{$opid}}{{.NameTag}}RequestBody + if err := runtime.BindForm(&body, form, nil, nil); err != nil { + return err + } + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + } else { + return err + } + {{else if eq .NameTag "Multipart" -}} + {{if eq .ContentType "multipart/form-data" -}} + if reader, err := ctx.Request().MultipartReader(); err != nil { + return err + } else { + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = reader + } + {{else -}} + if _, params, err := mime.ParseMediaType(ctx.Request().Header.Get("Content-Type")); err != nil { + return err + } else if boundary := params["boundary"]; boundary == "" { + return http.ErrMissingBoundary + } else { + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(ctx.Request().Body, boundary) + } + {{end -}} + {{else if eq .NameTag "Text" -}} + data, err := io.ReadAll(ctx.Request().Body) + if err != nil { + return err + } + body := {{$opid}}{{.NameTag}}RequestBody(data) + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{else -}} + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = ctx.Request().Body + {{end}}{{/* if eq .NameTag "JSON" */ -}} + {{if $multipleBodies}}}{{end}} + {{end}}{{/* range .Bodies */}} + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error){ + return sh.ssi.{{.OperationId}}(ctx.Request().Context(), request.({{$opid | ucFirst}}RequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "{{.OperationId}}") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.({{$opid | ucFirst}}ResponseObject); ok { + return validResponse.Visit{{$opid}}Response(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil + } + {{end}} +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-interface.tmpl index 8bcfc89b1..98ce54271 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-interface.tmpl @@ -1,4 +1,4 @@ -{{range .}} +{{range .}}{{if not .IsAlias}} {{$opid := .OperationId -}} type {{$opid | ucFirst}}RequestObject struct { {{range .PathParams -}} @@ -26,13 +26,14 @@ {{$fixedStatusCode := .HasFixedStatusCode -}} {{$isRef := .IsRef -}} {{$isExternalRef := .IsExternalRef -}} - {{$ref := .Ref | ucFirst -}} + {{$ref := .Ref | ucFirstWithPkgName -}} {{$headers := .Headers -}} {{if (and $hasHeaders (not $isRef)) -}} type {{$opid}}{{$statusCode}}ResponseHeaders struct { {{range .Headers -}} - {{.GoName}} {{.Schema.TypeDecl}} + {{with .DeprecationComment}}{{.}} + {{end -}}{{.GoName}} {{.GoTypeDef}} {{end -}} } {{end}} @@ -40,15 +41,23 @@ {{range .Contents}} {{$receiverTypeName := printf "%s%s%s%s" $opid $statusCode .NameTagOrContentType "Response"}} {{if and $fixedStatusCode $isRef -}} - {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (eq .NameTag "Multipart") -}} + {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (or (eq .NameTag "Multipart") (eq .NameTag "Text")) -}} type {{$receiverTypeName}} {{$ref}}{{.NameTagOrContentType}}Response - {{else if $isExternalRef -}} - type {{$receiverTypeName}} struct { {{$ref}} } {{else -}} type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } {{end}} {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}} type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if and .Schema.IsRef (not .Schema.IsExternalRef)}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} + {{- if and .IsJSON .Schema.HasCustomMarshalJSON}} + + func (t {{$receiverTypeName}}) MarshalJSON() ([]byte, error) { + return {{.Schema.TypeDecl}}(t).MarshalJSON() + } + + func (t *{{$receiverTypeName}}) UnmarshalJSON(b []byte) error { + return (*{{.Schema.TypeDecl}})(t).UnmarshalJSON(b) + } + {{- end}} {{else -}} type {{$receiverTypeName}} struct { Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}} @@ -72,12 +81,22 @@ func (response {{$receiverTypeName}}) Visit{{$opid}}Response(ctx *fiber.Ctx) error { {{range $headers -}} - ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} {{end -}} {{if eq .NameTag "Multipart" -}} writer := multipart.NewWriter(ctx.Response().BodyWriter()) {{end -}} - ctx.Response().Header.Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType("{{.ContentType}}", map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) + ctx.Response().Header.Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType({{.ContentType | toGoString}}, map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}{{.ContentType | toGoString}}{{else}}response.ContentType{{end}}) {{if not .IsSupported -}} if response.ContentLength != 0 { ctx.Response().Header.Set("Content-Length", fmt.Sprint(response.ContentLength)) @@ -87,9 +106,9 @@ {{$hasBodyVar := or ($hasHeaders) (not $fixedStatusCode) (not .IsSupported)}} {{if .IsJSON }} {{$hasUnionElements := ne 0 (len .Schema.UnionElements)}} - return ctx.JSON(&{{if $hasBodyVar}}response.Body{{else}}response{{end}}{{if $hasUnionElements}}.union{{end}}) + return ctx.JSON(&{{if $hasBodyVar}}response.Body{{else}}response{{end}}{{if and $hasUnionElements (not .Schema.IsExternalRef)}}.union{{end}}) {{else if eq .NameTag "Text" -}} - _, err := ctx.WriteString(string({{if $hasBodyVar}}response.Body{{else}}response{{end}})) + _, err := ctx.WriteString(fmt.Sprint({{if $hasBodyVar}}response.Body{{else}}response{{end}})) return err {{else if eq .NameTag "Formdata" -}} if form, err := runtime.MarshalForm({{if $hasBodyVar}}response.Body{{else}}response{{end}}, nil); err != nil { @@ -102,11 +121,38 @@ defer writer.Close() return {{if $hasBodyVar}}response.Body{{else}}response{{end}}(writer); {{else -}} - if closer, ok := response.Body.(io.ReadCloser); ok { - defer closer.Close() - } - _, err := io.Copy(ctx.Response().BodyWriter(), response.Body) - return err + {{if .IsStreamingContentType -}} + // Fiber/fasthttp streams through a callback: fasthttp emits + // a chunk each time we call w.Flush(), so clients see + // streaming data immediately instead of waiting on buffering. + ctx.Response().SetBodyStreamWriter(func(w *bufio.Writer) { + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + buf := make([]byte, 4096) + for { + n, err := response.Body.Read(buf) + if n > 0 { + if _, writeErr := w.Write(buf[:n]); writeErr != nil { + return + } + if flushErr := w.Flush(); flushErr != nil { + return + } + } + if err != nil { + return + } + } + }) + return nil + {{else -}} + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(ctx.Response().BodyWriter(), response.Body) + return err + {{end}}{{/* if .IsStreamingContentType */ -}} {{end}}{{/* if eq .NameTag "JSON" */ -}} } {{end}} @@ -126,20 +172,32 @@ {{end -}} func (response {{$opid}}{{$statusCode}}Response) Visit{{$opid}}Response(ctx *fiber.Ctx) error { {{range $headers -}} - ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} {{end -}} ctx.Status({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) return nil } {{end}} {{end}} -{{end}} +{{end}}{{end}} // StrictServerInterface represents all server handlers. type StrictServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{$opid := .OperationId -}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{$opid := .OperationId -}} {{$opid}}(ctx context.Context, request {{$opid | ucFirst}}RequestObject) ({{$opid | ucFirst}}ResponseObject, error) -{{end}}{{/* range . */ -}} +{{end}}{{end}}{{/* range . */ -}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-v3-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-v3-interface.tmpl new file mode 100644 index 000000000..b93c5d0f0 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-v3-interface.tmpl @@ -0,0 +1,203 @@ +{{range .}}{{if not .IsAlias}} + {{$opid := .OperationId -}} + type {{$opid | ucFirst}}RequestObject struct { + {{range .PathParams -}} + {{.GoName | ucFirst}} {{.TypeDef}} {{.JsonTag}} + {{end -}} + {{if .RequiresParamObject -}} + Params {{$opid}}Params + {{end -}} + {{if .HasMaskedRequestContentTypes -}} + ContentType string + {{end -}} + {{$multipleBodies := gt (len .Bodies) 1 -}} + {{range .Bodies -}} + {{if $multipleBodies}}{{.NameTag}}{{end}}Body {{if eq .NameTag "Multipart"}}*multipart.Reader{{else if ne .NameTag ""}}*{{$opid}}{{.NameTag}}RequestBody{{else}}io.Reader{{end}} + {{end -}} + } + + type {{$opid | ucFirst}}ResponseObject interface { + Visit{{$opid}}Response(ctx fiber.Ctx) error + } + + {{range .Responses}} + {{$statusCode := .StatusCode -}} + {{$hasHeaders := ne 0 (len .Headers) -}} + {{$fixedStatusCode := .HasFixedStatusCode -}} + {{$isRef := .IsRef -}} + {{$isExternalRef := .IsExternalRef -}} + {{$ref := .Ref | ucFirstWithPkgName -}} + {{$headers := .Headers -}} + + {{if (and $hasHeaders (not $isRef)) -}} + type {{$opid}}{{$statusCode}}ResponseHeaders struct { + {{range .Headers -}} + {{with .DeprecationComment}}{{.}} + {{end -}}{{.GoName}} {{.GoTypeDef}} + {{end -}} + } + {{end}} + + {{range .Contents}} + {{$receiverTypeName := printf "%s%s%s%s" $opid $statusCode .NameTagOrContentType "Response"}} + {{if and $fixedStatusCode $isRef -}} + {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (or (eq .NameTag "Multipart") (eq .NameTag "Text")) -}} + type {{$receiverTypeName}} {{$ref}}{{.NameTagOrContentType}}Response + {{else -}} + type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } + {{end}} + {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}} + type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if and .Schema.IsRef (not .Schema.IsExternalRef)}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} + {{- if and .IsJSON .Schema.HasCustomMarshalJSON}} + + func (t {{$receiverTypeName}}) MarshalJSON() ([]byte, error) { + return {{.Schema.TypeDecl}}(t).MarshalJSON() + } + + func (t *{{$receiverTypeName}}) UnmarshalJSON(b []byte) error { + return (*{{.Schema.TypeDecl}})(t).UnmarshalJSON(b) + } + {{- end}} + {{else -}} + type {{$receiverTypeName}} struct { + Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}} + {{if $hasHeaders -}} + Headers {{if $isRef}}{{$ref}}{{else}}{{$opid}}{{$statusCode}}{{end}}ResponseHeaders + {{end -}} + + {{if not $fixedStatusCode -}} + StatusCode int + {{end -}} + + {{if not .HasFixedContentType -}} + ContentType string + {{end -}} + + {{if not .IsSupported -}} + ContentLength int64 + {{end -}} + } + {{end}} + + func (response {{$receiverTypeName}}) Visit{{$opid}}Response(ctx fiber.Ctx) error { + {{range $headers -}} + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} + {{end -}} + {{if eq .NameTag "Multipart" -}} + writer := multipart.NewWriter(ctx.Response().BodyWriter()) + {{end -}} + ctx.Response().Header.Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType({{.ContentType | toGoString}}, map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}{{.ContentType | toGoString}}{{else}}response.ContentType{{end}}) + {{if not .IsSupported -}} + if response.ContentLength != 0 { + ctx.Response().Header.Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + {{end -}} + ctx.Status({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) + {{$hasBodyVar := or ($hasHeaders) (not $fixedStatusCode) (not .IsSupported)}} + {{if .IsJSON }} + {{$hasUnionElements := ne 0 (len .Schema.UnionElements)}} + return ctx.JSON(&{{if $hasBodyVar}}response.Body{{else}}response{{end}}{{if and $hasUnionElements (not .Schema.IsExternalRef)}}.union{{end}}) + {{else if eq .NameTag "Text" -}} + _, err := ctx.WriteString(fmt.Sprint({{if $hasBodyVar}}response.Body{{else}}response{{end}})) + return err + {{else if eq .NameTag "Formdata" -}} + if form, err := runtime.MarshalForm({{if $hasBodyVar}}response.Body{{else}}response{{end}}, nil); err != nil { + return err + } else { + _, err := ctx.WriteString(form.Encode()) + return err + } + {{else if eq .NameTag "Multipart" -}} + defer writer.Close() + return {{if $hasBodyVar}}response.Body{{else}}response{{end}}(writer); + {{else -}} + {{if .IsStreamingContentType -}} + // Fiber/fasthttp streams through a callback: fasthttp emits + // a chunk each time we call w.Flush(), so clients see + // streaming data immediately instead of waiting on buffering. + ctx.Response().SetBodyStreamWriter(func(w *bufio.Writer) { + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + buf := make([]byte, 4096) + for { + n, err := response.Body.Read(buf) + if n > 0 { + if _, writeErr := w.Write(buf[:n]); writeErr != nil { + return + } + if flushErr := w.Flush(); flushErr != nil { + return + } + } + if err != nil { + return + } + } + }) + return nil + {{else -}} + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(ctx.Response().BodyWriter(), response.Body) + return err + {{end}}{{/* if .IsStreamingContentType */ -}} + {{end}}{{/* if eq .NameTag "JSON" */ -}} + } + {{end}} + + {{if eq 0 (len .Contents) -}} + {{if and $fixedStatusCode $isRef -}} + type {{$opid}}{{$statusCode}}Response {{if not $isExternalRef}}={{end}} {{$ref}}Response + {{else -}} + type {{$opid}}{{$statusCode}}Response struct { + {{if $hasHeaders -}} + Headers {{if $isRef}}{{$ref}}{{else}}{{$opid}}{{$statusCode}}{{end}}ResponseHeaders + {{end}} + {{if not $fixedStatusCode -}} + StatusCode int + {{end -}} + } + {{end -}} + func (response {{$opid}}{{$statusCode}}Response) Visit{{$opid}}Response(ctx fiber.Ctx) error { + {{range $headers -}} + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} + {{end -}} + ctx.Status({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) + return nil + } + {{end}} + {{end}} +{{end}}{{end}} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} +// ({{.Method}} {{.Path}}) +{{with .DeprecationComment}}// +{{.}} +{{end}}{{$opid := .OperationId -}} +{{$opid}}(ctx context.Context, request {{$opid | ucFirst}}RequestObject) ({{$opid | ucFirst}}ResponseObject, error) +{{end}}{{end}}{{/* range . */ -}} +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-v3.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-v3.tmpl new file mode 100644 index 000000000..d4f154258 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber-v3.tmpl @@ -0,0 +1,92 @@ +type StrictHandlerFunc func(ctx fiber.Ctx, args any) (any, error) + +type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +{{range .}} + {{if not .IsAlias}} + {{$opid := .OperationId}} + // {{$opid}} operation middleware + func (sh *strictHandler) {{.OperationId}}(ctx fiber.Ctx{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error { + var request {{$opid | ucFirst}}RequestObject + + {{range .PathParams -}} + {{$varName := .GoVariableName -}} + request.{{.GoName}} = {{.GoVariableName}} + {{end -}} + + {{if .RequiresParamObject -}} + request.Params = params + {{end -}} + + {{ if .HasMaskedRequestContentTypes -}} + request.ContentType = string(ctx.Request().Header.ContentType()) + {{end -}} + + {{$multipleBodies := gt (len .Bodies) 1 -}} + {{range .Bodies -}} + {{if $multipleBodies}}if strings.HasPrefix(string(ctx.Request().Header.ContentType()), "{{.ContentType}}") { {{end}} + {{if .IsJSON }} + var body {{$opid}}{{.NameTag}}RequestBody + if err := ctx.Bind().Body(&body); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{else if eq .NameTag "Formdata" -}} + var body {{$opid}}{{.NameTag}}RequestBody + if err := ctx.Bind().Body(&body); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{else if eq .NameTag "Multipart" -}} + {{if eq .ContentType "multipart/form-data" -}} + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(bytes.NewReader(ctx.Request().Body()), string(ctx.Request().Header.MultipartFormBoundary())) + {{else -}} + if _, params, err := mime.ParseMediaType(string(ctx.Request().Header.ContentType())); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else if boundary := params["boundary"]; boundary == "" { + return fiber.NewError(fiber.StatusBadRequest, http.ErrMissingBoundary.Error()) + } else { + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(bytes.NewReader(ctx.Request().Body()), boundary) + } + {{end -}} + {{else if eq .NameTag "Text" -}} + data := ctx.Request().Body() + body := {{$opid}}{{.NameTag}}RequestBody(data) + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{else -}} + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = bytes.NewReader(ctx.Request().Body()) + {{end}}{{/* if eq .NameTag "JSON" */ -}} + {{if $multipleBodies}}}{{end}} + {{end}}{{/* range .Bodies */}} + + handler := func(ctx fiber.Ctx, request interface{}) (interface{}, error) { + return sh.ssi.{{.OperationId}}(ctx.Context(), request.({{$opid | ucFirst}}RequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "{{.OperationId}}") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.({{$opid | ucFirst}}ResponseObject); ok { + if err := validResponse.Visit{{$opid}}Response(ctx); err != nil { + return err + } + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil + } + {{end}} +{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber.tmpl index 510899c9e..2bd4e6bf7 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-fiber.tmpl @@ -1,5 +1,4 @@ -type StrictHandlerFunc func(ctx *fiber.Ctx, args interface{}) (interface{}, error) - +type StrictHandlerFunc func(ctx *fiber.Ctx, args any) (any, error) type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { @@ -13,6 +12,7 @@ type strictHandler struct { {{range .}} {{$opid := .OperationId}} + {{if not .IsAlias}} // {{$opid}} operation middleware func (sh *strictHandler) {{.OperationId}}(ctx *fiber.Ctx{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error { var request {{$opid | ucFirst}}RequestObject @@ -32,13 +32,20 @@ type strictHandler struct { {{$multipleBodies := gt (len .Bodies) 1 -}} {{range .Bodies -}} - {{if $multipleBodies}}if strings.HasPrefix(string(ctx.Request().Header.ContentType()), "{{.ContentType}}") { {{end}} + {{if $multipleBodies}}if strings.HasPrefix(string(ctx.Request().Header.ContentType()), {{.ContentType | toGoString}}) { {{end}} {{if .IsJSON }} var body {{$opid}}{{.NameTag}}RequestBody if err := ctx.BodyParser(&body); err != nil { + {{if not .Required -}} + if !errors.Is(err, io.EOF) { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + {{else -}} return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } + {{end -}} + } {{if not .Required -}} else { {{end}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} } {{end}} {{else if eq .NameTag "Formdata" -}} var body {{$opid}}{{.NameTag}}RequestBody if err := ctx.BodyParser(&body); err != nil { @@ -59,8 +66,14 @@ type strictHandler struct { {{end -}} {{else if eq .NameTag "Text" -}} data := ctx.Request().Body() + {{if not .Required -}} + if len(data) > 0 { + {{end -}} body := {{$opid}}{{.NameTag}}RequestBody(data) request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} + } + {{end -}} {{else -}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = bytes.NewReader(ctx.Request().Body()) {{end}}{{/* if eq .NameTag "JSON" */ -}} @@ -77,14 +90,15 @@ type strictHandler struct { response, err := handler(ctx, request) if err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) + return err } else if validResponse, ok := response.({{$opid | ucFirst}}ResponseObject); ok { if err := validResponse.Visit{{$opid}}Response(ctx); err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) + return err } } else if response != nil { return fmt.Errorf("unexpected response type: %T", response) } return nil } + {{end}} {{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-gin.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-gin.tmpl index d4c43164a..ed2e561a5 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-gin.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-gin.tmpl @@ -1,17 +1,63 @@ -type StrictHandlerFunc = strictgin.StrictGinHandlerFunc -type StrictMiddlewareFunc = strictgin.StrictGinMiddlewareFunc +type StrictHandlerFunc func(ctx *gin.Context, request any) (any, error) +type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc + +type StrictGinServerOptions struct { + // RequestErrorHandlerFunc is called when a request cannot be parsed or + // decoded. It is invoked for JSON bind failures, form parse/bind errors, + // multipart reader errors, media type parse errors, missing multipart + // boundaries, and request body read errors. The default returns 400. + RequestErrorHandlerFunc func(ctx *gin.Context, err error) + // HandlerErrorFunc is called when the application handler (or any + // middleware wrapping it) returns a non-nil error. The default returns 500. + HandlerErrorFunc func(ctx *gin.Context, err error) + // ResponseErrorHandlerFunc is called when the response object fails to + // serialize (Visit*Response returns an error) or when the handler returns + // an unexpected response type. The default returns 500. + ResponseErrorHandlerFunc func(ctx *gin.Context, err error) +} func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { - return &strictHandler{ssi: ssi, middlewares: middlewares} + return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictGinServerOptions { + RequestErrorHandlerFunc: func(ctx *gin.Context, err error) { + ctx.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()}) + }, + HandlerErrorFunc: func(ctx *gin.Context, err error) { + ctx.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()}) + }, + ResponseErrorHandlerFunc: func(ctx *gin.Context, err error) { + ctx.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()}) + }, + }} +} + +func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictGinServerOptions) ServerInterface { + if options.RequestErrorHandlerFunc == nil { + options.RequestErrorHandlerFunc = func(ctx *gin.Context, err error) { + ctx.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()}) + } + } + if options.HandlerErrorFunc == nil { + options.HandlerErrorFunc = func(ctx *gin.Context, err error) { + ctx.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()}) + } + } + if options.ResponseErrorHandlerFunc == nil { + options.ResponseErrorHandlerFunc = func(ctx *gin.Context, err error) { + ctx.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()}) + } + } + return &strictHandler{ssi: ssi, middlewares: middlewares, options: options} } type strictHandler struct { ssi StrictServerInterface middlewares []StrictMiddlewareFunc + options StrictGinServerOptions } {{range .}} {{$opid := .OperationId}} + {{if not .IsAlias}} // {{$opid}} operation middleware func (sh *strictHandler) {{.OperationId}}(ctx *gin.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) { var request {{$opid | ucFirst}}RequestObject @@ -30,40 +76,47 @@ type strictHandler struct { {{$multipleBodies := gt (len .Bodies) 1 -}} {{range .Bodies -}} - {{if $multipleBodies}}if strings.HasPrefix(ctx.GetHeader("Content-Type"), "{{.ContentType}}") { {{end}} + {{if $multipleBodies}}if strings.HasPrefix(ctx.GetHeader("Content-Type"), {{.ContentType | toGoString}}) { {{end}} {{if .IsJSON }} var body {{$opid}}{{.NameTag}}RequestBody if err := ctx.ShouldBindJSON(&body); err != nil { - ctx.Status(http.StatusBadRequest) - ctx.Error(err) + {{if not .Required -}} + if !errors.Is(err, io.EOF) { + sh.options.RequestErrorHandlerFunc(ctx, err) + return + } + {{else -}} + sh.options.RequestErrorHandlerFunc(ctx, err) return - } + {{end -}} + } {{if not .Required -}} else { {{end}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} } {{end}} {{else if eq .NameTag "Formdata" -}} if err := ctx.Request.ParseForm(); err != nil { - ctx.Error(err) + sh.options.RequestErrorHandlerFunc(ctx, err) return } var body {{$opid}}{{.NameTag}}RequestBody if err := runtime.BindForm(&body, ctx.Request.Form, nil, nil); err != nil { - ctx.Error(err) + sh.options.RequestErrorHandlerFunc(ctx, err) return } request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body {{else if eq .NameTag "Multipart" -}} {{if eq .ContentType "multipart/form-data" -}} - if reader, err := ctx.Request.MultipartReader(); err == nil { - request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = reader - } else { - ctx.Error(err) + if reader, err := ctx.Request.MultipartReader(); err != nil { + sh.options.RequestErrorHandlerFunc(ctx, err) return + } else { + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = reader } {{else -}} if _, params, err := mime.ParseMediaType(ctx.Request.Header.Get("Content-Type")); err != nil { - ctx.Error(err) + sh.options.RequestErrorHandlerFunc(ctx, err) return } else if boundary := params["boundary"]; boundary == "" { - ctx.Error(http.ErrMissingBoundary) + sh.options.RequestErrorHandlerFunc(ctx, http.ErrMissingBoundary) return } else { request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(ctx.Request.Body, boundary) @@ -72,11 +125,17 @@ type strictHandler struct { {{else if eq .NameTag "Text" -}} data, err := io.ReadAll(ctx.Request.Body) if err != nil { - ctx.Error(err) + sh.options.RequestErrorHandlerFunc(ctx, err) return } + {{if not .Required -}} + if len(data) > 0 { + {{end -}} body := {{$opid}}{{.NameTag}}RequestBody(data) request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} + } + {{end -}} {{else -}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = ctx.Request.Body {{end}}{{/* if eq .NameTag "JSON" */ -}} @@ -93,14 +152,14 @@ type strictHandler struct { response, err := handler(ctx, request) if err != nil { - ctx.Error(err) - ctx.Status(http.StatusInternalServerError) + sh.options.HandlerErrorFunc(ctx, err) } else if validResponse, ok := response.({{$opid | ucFirst}}ResponseObject); ok { if err := validResponse.Visit{{$opid}}Response(ctx.Writer); err != nil { - ctx.Error(err) + sh.options.ResponseErrorHandlerFunc(ctx, err) } } else if response != nil { - ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(ctx, fmt.Errorf("unexpected response type: %T", response)) } } + {{end}} {{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-http.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-http.tmpl index 8d32415ad..8e6166cc9 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-http.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-http.tmpl @@ -1,5 +1,5 @@ -type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc -type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc +type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, request any) (any, error) +type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc type StrictHTTPServerOptions struct { RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) @@ -18,6 +18,16 @@ func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareF } func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictHTTPServerOptions) ServerInterface { + if options.RequestErrorHandlerFunc == nil { + options.RequestErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + if options.ResponseErrorHandlerFunc == nil { + options.ResponseErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } return &strictHandler{ssi: ssi, middlewares: middlewares, options: options} } @@ -29,6 +39,7 @@ type strictHandler struct { {{range .}} {{$opid := .OperationId}} + {{if not .IsAlias}} // {{$opid}} operation middleware func (sh *strictHandler) {{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) { var request {{$opid | ucFirst}}RequestObject @@ -47,14 +58,22 @@ type strictHandler struct { {{$multipleBodies := gt (len .Bodies) 1 -}} {{range .Bodies -}} - {{if $multipleBodies}}if strings.HasPrefix(r.Header.Get("Content-Type"), "{{.ContentType}}") { {{end}} + {{if $multipleBodies}}if strings.HasPrefix(r.Header.Get("Content-Type"), {{.ContentType | toGoString}}) { {{end}} {{if .IsJSON }} var body {{$opid}}{{.NameTag}}RequestBody if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + {{if not .Required -}} + if !errors.Is(err, io.EOF) { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + {{else -}} sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) return - } + {{end -}} + } {{if not .Required -}} else { {{end}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} } {{end}} {{else if eq .NameTag "Formdata" -}} if err := r.ParseForm(); err != nil { sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode formdata: %w", err)) @@ -91,8 +110,14 @@ type strictHandler struct { sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't read body: %w", err)) return } + {{if not .Required -}} + if len(data) > 0 { + {{end -}} body := {{$opid}}{{.NameTag}}RequestBody(data) request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} + } + {{end -}} {{else -}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = r.Body {{end}}{{/* if eq .NameTag "JSON" */ -}} @@ -118,4 +143,5 @@ type strictHandler struct { sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) } } + {{end}} {{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-interface.tmpl index e19936d6a..1431ac9c0 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-interface.tmpl @@ -1,4 +1,4 @@ -{{range .}} +{{range .}}{{if not .IsAlias}} {{$opid := .OperationId -}} type {{$opid | ucFirst}}RequestObject struct { {{range .PathParams -}} @@ -32,7 +32,8 @@ {{if (and $hasHeaders (not $isRef)) -}} type {{$opid}}{{$statusCode}}ResponseHeaders struct { {{range .Headers -}} - {{.GoName}} {{.Schema.TypeDecl}} + {{with .DeprecationComment}}{{.}} + {{end -}}{{.GoName}} {{.GoTypeDef}} {{end -}} } {{end}} @@ -40,15 +41,23 @@ {{range .Contents}} {{$receiverTypeName := printf "%s%s%s%s" $opid $statusCode .NameTagOrContentType "Response"}} {{if and $fixedStatusCode $isRef -}} - {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (eq .NameTag "Multipart") -}} + {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (or (eq .NameTag "Multipart") (eq .NameTag "Text")) -}} type {{$receiverTypeName}} {{$ref}}{{.NameTagOrContentType}}Response - {{else if $isExternalRef -}} - type {{$receiverTypeName}} struct { {{$ref}} } {{else -}} type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } {{end}} {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}} type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if and .Schema.IsRef (not .Schema.IsExternalRef)}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} + {{- if and .IsJSON .Schema.HasCustomMarshalJSON}} + + func (t {{$receiverTypeName}}) MarshalJSON() ([]byte, error) { + return {{.Schema.TypeDecl}}(t).MarshalJSON() + } + + func (t *{{$receiverTypeName}}) UnmarshalJSON(b []byte) error { + return (*{{.Schema.TypeDecl}})(t).UnmarshalJSON(b) + } + {{- end}} {{else -}} type {{$receiverTypeName}} struct { Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}} @@ -74,40 +83,116 @@ {{if eq .NameTag "Multipart" -}} writer := multipart.NewWriter(w) {{end -}} - w.Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType("{{.ContentType}}", map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) - {{if not .IsSupported -}} - if response.ContentLength != 0 { - w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) - } - {{end -}} - {{range $headers -}} - w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) - {{end -}} - w.WriteHeader({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) {{$hasBodyVar := or ($hasHeaders) (not $fixedStatusCode) (not .IsSupported)}} {{if .IsJSON -}} - {{$hasUnionElements := ne 0 (len .Schema.UnionElements)}} - return json.NewEncoder(w).Encode(response{{if $hasBodyVar}}.Body{{end}}{{if $hasUnionElements}}.union{{end}}) - {{else if eq .NameTag "Text" -}} - _, err := w.Write([]byte({{if $hasBodyVar}}response.Body{{else}}response{{end}})) + {{$hasUnionElements := ne 0 (len .Schema.UnionElements) -}} + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response{{if $hasBodyVar}}.Body{{end}}{{if and $hasUnionElements (not .Schema.IsExternalRef)}}.union{{end}}); err != nil { + return err + } + w.Header().Set("Content-Type", {{if .HasFixedContentType }}{{.ContentType | toGoString}}{{else}}response.ContentType{{end}}) + {{range $headers -}} + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + w.Header().Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} + {{end -}} + w.WriteHeader({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) + _, err := buf.WriteTo(w) return err {{else if eq .NameTag "Formdata" -}} - if form, err := runtime.MarshalForm({{if $hasBodyVar}}response.Body{{else}}response{{end}}, nil); err != nil { + form, err := runtime.MarshalForm({{if $hasBodyVar}}response.Body{{else}}response{{end}}, nil) + if err != nil { return err - } else { - _, err := w.Write([]byte(form.Encode())) - return err - } - {{else if eq .NameTag "Multipart" -}} - defer writer.Close() - return {{if $hasBodyVar}}response.Body{{else}}response{{end}}(writer); - {{else -}} - if closer, ok := response.Body.(io.ReadCloser); ok { - defer closer.Close() } - _, err := io.Copy(w, response.Body) + w.Header().Set("Content-Type", {{if .HasFixedContentType }}{{.ContentType | toGoString}}{{else}}response.ContentType{{end}}) + {{range $headers -}} + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + w.Header().Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} + {{end -}} + w.WriteHeader({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) + _, err = w.Write([]byte(form.Encode())) return err - {{end}}{{/* if eq .NameTag "JSON" */ -}} + {{else -}} + w.Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType({{.ContentType | toGoString}}, map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}{{.ContentType | toGoString}}{{else}}response.ContentType{{end}}) + {{if not .IsSupported -}} + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + {{end -}} + {{range $headers -}} + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + w.Header().Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} + {{end -}} + w.WriteHeader({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) + + {{if eq .NameTag "Text" -}} + _, err := w.Write([]byte(fmt.Sprint({{if $hasBodyVar}}response.Body{{else}}response{{end}}))) + return err + {{else if eq .NameTag "Multipart" -}} + defer writer.Close() + return {{if $hasBodyVar}}response.Body{{else}}response{{end}}(writer); + {{else -}} + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + {{if .IsStreamingContentType -}} + flusher, ok := w.(http.Flusher) + if !ok { + // If w doesn't support flushing, fall back to io.Copy. + _, err := io.Copy(w, response.Body) + return err + } + // text/event-stream messages are typically small; use a + // modest buffer and flush after each chunk so clients see + // events immediately instead of waiting on OS buffering. + buf := make([]byte, 4096) + for { + n, err := response.Body.Read(buf) + if n > 0 { + if _, writeErr := w.Write(buf[:n]); writeErr != nil { + return writeErr + } + flusher.Flush() + } + if err != nil { + if err == io.EOF { + return nil + } + return err + } + } + {{else -}} + _, err := io.Copy(w, response.Body) + return err + {{end}}{{/* if .IsStreamingContentType */ -}} + {{end}}{{/* if eq .NameTag "Text" */ -}} + {{end}}{{/* if .IsJSON */ -}} } {{end}} @@ -126,20 +211,32 @@ {{end -}} func (response {{$opid}}{{$statusCode}}Response) Visit{{$opid}}Response(w http.ResponseWriter) error { {{range $headers -}} - w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + w.Header().Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} {{end -}} w.WriteHeader({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) return nil } {{end}} {{end}} -{{end}} +{{end}}{{end}} // StrictServerInterface represents all server handlers. type StrictServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{$opid := .OperationId -}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{$opid := .OperationId -}} {{$opid}}(ctx context.Context, request {{$opid | ucFirst}}RequestObject) ({{$opid | ucFirst}}ResponseObject, error) -{{end}}{{/* range . */ -}} +{{end}}{{end}}{{/* range . */ -}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-iris-interface.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-iris-interface.tmpl index e93fdf616..77dffa860 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-iris-interface.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-iris-interface.tmpl @@ -1,4 +1,4 @@ -{{range .}} +{{range .}}{{if not .IsAlias}} {{$opid := .OperationId -}} type {{$opid | ucFirst}}RequestObject struct { {{range .PathParams -}} @@ -32,25 +32,32 @@ {{if (and $hasHeaders (not $isRef)) -}} type {{$opid}}{{$statusCode}}ResponseHeaders struct { {{range .Headers -}} - {{.GoName}} {{.Schema.TypeDecl}} + {{with .DeprecationComment}}{{.}} + {{end -}}{{.GoName}} {{.GoTypeDef}} {{end -}} } {{end}} {{range .Contents}} {{$receiverTypeName := printf "%s%s%s%s" $opid $statusCode .NameTagOrContentType "Response"}} - {{if eq .NameTag "Text" -}} - type {{$receiverTypeName}} string - {{else if and $fixedStatusCode $isRef -}} - {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (eq .NameTag "Multipart") -}} + {{if and $fixedStatusCode $isRef -}} + {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (or (eq .NameTag "Multipart") (eq .NameTag "Text")) -}} type {{$receiverTypeName}} {{$ref}}{{.NameTagOrContentType}}Response - {{else if $isExternalRef -}} - type {{$receiverTypeName}} struct { {{$ref}} } {{else -}} type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } {{end}} {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}} type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if and .Schema.IsRef (not .Schema.IsExternalRef)}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} + {{- if and .IsJSON .Schema.HasCustomMarshalJSON}} + + func (t {{$receiverTypeName}}) MarshalJSON() ([]byte, error) { + return {{.Schema.TypeDecl}}(t).MarshalJSON() + } + + func (t *{{$receiverTypeName}}) UnmarshalJSON(b []byte) error { + return (*{{.Schema.TypeDecl}})(t).UnmarshalJSON(b) + } + {{- end}} {{else -}} type {{$receiverTypeName}} struct { Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}} @@ -74,12 +81,22 @@ func (response {{$receiverTypeName}}) Visit{{$opid}}Response(ctx iris.Context) error { {{range $headers -}} - ctx.ResponseWriter().Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + ctx.ResponseWriter().Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + ctx.ResponseWriter().Header().Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + ctx.ResponseWriter().Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} {{end -}} {{if eq .NameTag "Multipart" -}} writer := multipart.NewWriter(ctx.ResponseWriter()) {{end -}} - ctx.ResponseWriter().Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType("{{.ContentType}}", map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) + ctx.ResponseWriter().Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType({{.ContentType | toGoString}}, map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}{{.ContentType | toGoString}}{{else}}response.ContentType{{end}}) {{if not .IsSupported -}} if response.ContentLength != 0 { ctx.ResponseWriter().Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) @@ -89,9 +106,9 @@ {{$hasBodyVar := or ($hasHeaders) (not $fixedStatusCode) (not .IsSupported)}} {{if .IsJSON -}} {{$hasUnionElements := ne 0 (len .Schema.UnionElements)}} - return ctx.JSON(&{{if $hasBodyVar}}response.Body{{else}}response{{end}}{{if $hasUnionElements}}.union{{end}}) + return ctx.JSON(&{{if $hasBodyVar}}response.Body{{else}}response{{end}}{{if and $hasUnionElements (not .Schema.IsExternalRef)}}.union{{end}}) {{else if eq .NameTag "Text" -}} - _, err := ctx.WriteString(string({{if $hasBodyVar}}response.Body{{else}}response{{end}})) + _, err := ctx.WriteString(fmt.Sprint({{if $hasBodyVar}}response.Body{{else}}response{{end}})) return err {{else if eq .NameTag "Formdata" -}} if form, err := runtime.MarshalForm({{if $hasBodyVar}}response.Body{{else}}response{{end}}, nil); err != nil { @@ -107,8 +124,36 @@ if closer, ok := response.Body.(io.ReadCloser); ok { defer closer.Close() } - _, err := io.Copy(ctx.ResponseWriter(), response.Body) - return err + {{if .IsStreamingContentType -}} + flusher, ok := ctx.ResponseWriter().(http.Flusher) + if !ok { + // Fall back to buffered copy when the response writer + // doesn't support flushing. + _, err := io.Copy(ctx.ResponseWriter(), response.Body) + return err + } + // Flush after each chunk so streaming clients see data + // immediately instead of waiting on buffering. + buf := make([]byte, 4096) + for { + n, err := response.Body.Read(buf) + if n > 0 { + if _, writeErr := ctx.ResponseWriter().Write(buf[:n]); writeErr != nil { + return writeErr + } + flusher.Flush() + } + if err != nil { + if err == io.EOF { + return nil + } + return err + } + } + {{else -}} + _, err := io.Copy(ctx.ResponseWriter(), response.Body) + return err + {{end}}{{/* if .IsStreamingContentType */ -}} {{end}}{{/* if eq .NameTag "JSON" */ -}} } {{end}} @@ -128,20 +173,32 @@ {{end -}} func (response {{$opid}}{{$statusCode}}Response) Visit{{$opid}}Response(ctx iris.Context) error { {{range $headers -}} - ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{if .IsNullable -}} + if response.Headers.{{.GoName}}.IsSpecified() { + ctx.ResponseWriter().Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}.MustGet())) + } + {{else if .IsOptional -}} + if response.Headers.{{.GoName}} != nil { + ctx.ResponseWriter().Header().Set("{{.Name}}", fmt.Sprint(*response.Headers.{{.GoName}})) + } + {{else -}} + ctx.ResponseWriter().Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) + {{end -}} {{end -}} ctx.StatusCode({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) return nil } {{end}} {{end}} -{{end}} +{{end}}{{end}} // StrictServerInterface represents all server handlers. type StrictServerInterface interface { -{{range .}}{{.SummaryAsComment }} +{{range .}}{{if not .IsAlias}}{{.SummaryAsComment .OperationId }} // ({{.Method}} {{.Path}}) -{{$opid := .OperationId -}} +{{with .DeprecationComment}}// +{{.}} +{{end}}{{$opid := .OperationId -}} {{$opid}}(ctx context.Context, request {{$opid | ucFirst}}RequestObject) ({{$opid | ucFirst}}ResponseObject, error) -{{end}}{{/* range . */ -}} +{{end}}{{end}}{{/* range . */ -}} } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-iris.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-iris.tmpl index dfdeb1546..404db7e23 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-iris.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-iris.tmpl @@ -1,5 +1,5 @@ -type StrictHandlerFunc = strictiris.StrictIrisHandlerFunc -type StrictMiddlewareFunc = strictiris.StrictIrisMiddlewareFunc +type StrictHandlerFunc func(ctx iris.Context, request any) (any, error) +type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { return &strictHandler{ssi: ssi, middlewares: middlewares} @@ -12,6 +12,7 @@ type strictHandler struct { {{range .}} {{$opid := .OperationId}} + {{if not .IsAlias}} // {{$opid}} operation middleware func (sh *strictHandler) {{.OperationId}}(ctx iris.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) { var request {{$opid | ucFirst}}RequestObject @@ -30,14 +31,22 @@ type strictHandler struct { {{$multipleBodies := gt (len .Bodies) 1 -}} {{range .Bodies -}} - {{if $multipleBodies}}if strings.HasPrefix(ctx.GetHeader("Content-Type"), "{{.ContentType}}") { {{end}} + {{if $multipleBodies}}if strings.HasPrefix(ctx.GetHeader("Content-Type"), {{.ContentType | toGoString}}) { {{end}} {{if .IsJSON }} var body {{$opid}}{{.NameTag}}RequestBody if err := ctx.ReadJSON(&body); err != nil { + {{if not .Required -}} + if !errors.Is(err, io.EOF) { + ctx.StopWithError(http.StatusBadRequest, err) + return + } + {{else -}} ctx.StopWithError(http.StatusBadRequest, err) return - } + {{end -}} + } {{if not .Required -}} else { {{end}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} } {{end}} {{else if eq .NameTag "Formdata" -}} if err := ctx.Request().ParseForm(); err != nil { ctx.StopWithError(http.StatusBadRequest, err) @@ -74,8 +83,14 @@ type strictHandler struct { ctx.StopWithError(http.StatusBadRequest, err) return } + {{if not .Required -}} + if len(data) > 0 { + {{end -}} body := {{$opid}}{{.NameTag}}RequestBody(data) request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} + } + {{end -}} {{else -}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = ctx.Request().Body {{end}}{{/* if eq .NameTag "JSON" */ -}} @@ -92,16 +107,17 @@ type strictHandler struct { response, err := handler(ctx, request) if err != nil { - ctx.StopWithError(http.StatusBadRequest, err) + ctx.StopWithError(http.StatusInternalServerError, err) return } else if validResponse, ok := response.({{$opid | ucFirst}}ResponseObject); ok { if err := validResponse.Visit{{$opid}}Response(ctx); err != nil { - ctx.StopWithError(http.StatusBadRequest, err) + ctx.StopWithError(http.StatusInternalServerError, err) return } } else if response != nil { - ctx.Writef("Unexpected response type: %T", response) + ctx.StopWithError(http.StatusInternalServerError, fmt.Errorf("unexpected response type: %T", response)) return } } + {{end}} {{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-responses.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-responses.tmpl index d04cbd7d6..77cb520a4 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-responses.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/strict/strict-responses.tmpl @@ -4,7 +4,8 @@ {{if $hasHeaders -}} type {{$name}}ResponseHeaders struct { {{range .Headers -}} - {{.GoName}} {{.Schema.TypeDecl}} + {{with .DeprecationComment}}{{.}} + {{end -}}{{.GoName}} {{.GoTypeDef}} {{end -}} } {{end -}} @@ -12,6 +13,16 @@ {{range .Contents -}} {{if and (not $hasHeaders) (.IsSupported) -}} type {{$name}}{{.NameTagOrContentType}}Response {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if .Schema.IsRef}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} + {{- if and .IsJSON .Schema.HasCustomMarshalJSON}} + + func (t {{$name}}{{.NameTagOrContentType}}Response) MarshalJSON() ([]byte, error) { + return {{.Schema.TypeDecl}}(t).MarshalJSON() + } + + func (t *{{$name}}{{.NameTagOrContentType}}Response) UnmarshalJSON(b []byte) error { + return (*{{.Schema.TypeDecl}})(t).UnmarshalJSON(b) + } + {{- end}} {{else -}} type {{$name}}{{.NameTagOrContentType}}Response struct { Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/typedef.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/typedef.tmpl index be3112c0f..d06a1bd8e 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/typedef.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/typedef.tmpl @@ -1,4 +1,4 @@ {{range .Types}} -{{ if .Schema.Description }}{{ toGoComment .Schema.Description .TypeName }}{{ else }}// {{.TypeName}} defines model for {{.JsonName}}.{{ end }} +{{ .DocComment }} type {{.TypeName}} {{if .IsAlias }}={{end}} {{.Schema.TypeDecl}} {{end}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/union-and-additional-properties.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/union-and-additional-properties.tmpl index 1c48092cc..6ec69f5e8 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/union-and-additional-properties.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/union-and-additional-properties.tmpl @@ -54,12 +54,12 @@ func (a {{.TypeName}}) MarshalJSON() ([]byte, error) { } } {{range .Schema.Properties}} -{{if .HasOptionalPointer}}if a.{{.GoFieldName}} != nil { {{end}} +{{if .RequiresNilCheck}}if a.{{.GoFieldName}} != nil { {{end}} object["{{.JsonFieldName}}"], err = json.Marshal(a.{{.GoFieldName}}) if err != nil { return nil, fmt.Errorf("error marshaling '{{.JsonFieldName}}': %w", err) } -{{if .HasOptionalPointer}} }{{end}} +{{if .RequiresNilCheck}} }{{end}} {{end}} for fieldName, field := range a.AdditionalProperties { object[fieldName], err = json.Marshal(field) diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/union.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/union.tmpl index c0385f82e..a97ba863e 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/union.tmpl +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/union.tmpl @@ -1,9 +1,9 @@ {{range .Types}} {{$typeName := .TypeName -}} {{$discriminator := .Schema.Discriminator}} - {{$properties := .Schema.Properties -}} + {{$schema := .Schema -}} {{range .Schema.UnionElements}} - {{$element := . -}} + {{$stamp := $schema.DiscriminatorStampFor . -}} // As{{ .Method }} returns the union data inside the {{$typeName}} as a {{.}} func (t {{$typeName}}) As{{ .Method }}() ({{.}}, error) { var body {{.}} @@ -13,46 +13,45 @@ // From{{ .Method }} overwrites any union data inside the {{$typeName}} as the provided {{.}} func (t *{{$typeName}}) From{{ .Method }} (v {{.}}) error { - {{if $discriminator -}} - {{range $value, $type := $discriminator.Mapping -}} - {{if eq $type $element -}} - {{$hasProperty := false -}} - {{range $properties -}} - {{if eq .GoFieldName $discriminator.PropertyName -}} - t.{{$discriminator.PropertyName}} = "{{$value}}" - {{$hasProperty = true -}} - {{end -}} - {{end -}} - {{if not $hasProperty}}v.{{$discriminator.PropertyName}} = "{{$value}}"{{end}} - {{end -}} + {{if and $stamp $stamp.Property -}} + {{if $stamp.Property.IsPointer -}} + var discriminator {{$stamp.Property.Schema.TypeDecl}} = "{{$stamp.Value}}" + t.{{$stamp.Property.GoFieldName}} = &discriminator + {{else -}} + t.{{$stamp.Property.GoFieldName}} = "{{$stamp.Value}}" {{end -}} {{end -}} b, err := json.Marshal(v) + {{if $stamp -}} + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{{$stamp.JSONPatch}}`)) + {{end -}} t.union = b return err } // Merge{{ .Method }} performs a merge with any union data inside the {{$typeName}}, using the provided {{.}} func (t *{{$typeName}}) Merge{{ .Method }} (v {{.}}) error { - {{if $discriminator -}} - {{range $value, $type := $discriminator.Mapping -}} - {{if eq $type $element -}} - {{$hasProperty := false -}} - {{range $properties -}} - {{if eq .GoFieldName $discriminator.PropertyName -}} - t.{{$discriminator.PropertyName}} = "{{$value}}" - {{$hasProperty = true -}} - {{end -}} - {{end -}} - {{if not $hasProperty}}v.{{$discriminator.PropertyName}} = "{{$value}}"{{end}} - {{end -}} + {{if and $stamp $stamp.Property -}} + {{if $stamp.Property.IsPointer -}} + var discriminator {{$stamp.Property.Schema.TypeDecl}} = "{{$stamp.Value}}" + t.{{$stamp.Property.GoFieldName}} = &discriminator + {{else -}} + t.{{$stamp.Property.GoFieldName}} = "{{$stamp.Value}}" {{end -}} {{end -}} b, err := json.Marshal(v) if err != nil { return err } - + {{if $stamp -}} + b, err = runtime.JSONMerge(b, []byte(`{{$stamp.JSONPatch}}`)) + if err != nil { + return err + } + {{end}} merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err @@ -68,16 +67,17 @@ return discriminator.Discriminator, err } - {{if ne 0 (len $discriminator.Mapping)}} + {{$cases := $schema.DiscriminatorCases -}} + {{if $cases}} func (t {{.TypeName}}) ValueByDiscriminator() (interface{}, error) { discriminator, err := t.Discriminator() if err != nil { return nil, err } switch discriminator{ - {{range $value, $type := $discriminator.Mapping -}} - case "{{$value}}": - return t.As{{$type}}() + {{range $cases -}} + case "{{.Value}}": + return t.As{{.Method}}() {{end -}} default: return nil, errors.New("unknown discriminator value: "+discriminator) @@ -102,12 +102,12 @@ } } {{range .Schema.Properties}} - {{if .HasOptionalPointer}}if t.{{.GoFieldName}} != nil { {{end}} + {{if .RequiresNilCheck}}if t.{{.GoFieldName}} != nil { {{end}} object["{{.JsonFieldName}}"], err = json.Marshal(t.{{.GoFieldName}}) if err != nil { return nil, fmt.Errorf("error marshaling '{{.JsonFieldName}}': %w", err) } - {{if .HasOptionalPointer}} }{{end}} + {{if .RequiresNilCheck}} }{{end}} {{end -}} b, err = json.Marshal(object) {{end -}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/webhook-initiator.tmpl b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/webhook-initiator.tmpl new file mode 100644 index 000000000..032e3933e --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/templates/webhook-initiator.tmpl @@ -0,0 +1,235 @@ +// WebhookInitiator sends OpenAPI 3.1 webhook requests to target URLs. +// Modeled on the generated Client, but with no stored Server -- the full +// target URL is provided per-call by the caller (typically discovered +// from a subscription registration). +type WebhookInitiator struct { + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// WebhookInitiatorOption allows setting custom parameters during construction. +type WebhookInitiatorOption func(*WebhookInitiator) error + +// NewWebhookInitiator creates a new WebhookInitiator with reasonable defaults. +func NewWebhookInitiator(opts ...WebhookInitiatorOption) (*WebhookInitiator, error) { + initiator := WebhookInitiator{} + for _, o := range opts { + if err := o(&initiator); err != nil { + return nil, err + } + } + if initiator.Client == nil { + initiator.Client = &http.Client{} + } + return &initiator, nil +} + +// WithWebhookHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithWebhookHTTPClient(doer HttpRequestDoer) WebhookInitiatorOption { + return func(p *WebhookInitiator) error { + p.Client = doer + return nil + } +} + +// WithWebhookRequestEditorFn allows setting up a callback function, which +// will be called right before sending the webhook request. This can be +// used to mutate the request, e.g. to add signature headers. +func WithWebhookRequestEditorFn(fn RequestEditorFn) WebhookInitiatorOption { + return func(p *WebhookInitiator) error { + p.RequestEditors = append(p.RequestEditors, fn) + return nil + } +} + +func (p *WebhookInitiator) applyWebhookEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range p.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// WebhookInitiatorInterface is the interface specification for the webhook initiator. +type WebhookInitiatorInterface interface { +{{range . -}} +{{$hasParams := .RequiresParamObject -}} +{{$opid := .OperationId -}} + // {{$opid}}{{if .HasBody}}WithBody{{end}} fires the {{.WebhookName}} webhook{{if .HasBody}} with any body{{end}} + {{$opid}}{{if .HasBody}}WithBody{{end}}(ctx context.Context, targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*http.Response, error) +{{range .Bodies}} + {{if .IsSupportedByClient -}} + {{$opid}}{{.Suffix}}(ctx context.Context, targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*http.Response, error) + {{end -}} +{{end}}{{/* range .Bodies */}} +{{end}}{{/* range . */}} +} + + +{{/* Generate webhook initiator methods */}} +{{range . -}} +{{$hasParams := .RequiresParamObject -}} +{{$opid := .OperationId -}} + +func (p *WebhookInitiator) {{$opid}}{{if .HasBody}}WithBody{{end}}(ctx context.Context, targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*http.Response, error) { + req, err := New{{$opid}}WebhookRequest{{if .HasBody}}WithBody{{end}}(targetURL{{if $hasParams}}, params{{end}}{{if .HasBody}}, contentType, body{{end}}) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := p.applyWebhookEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return p.Client.Do(req) +} + +{{range .Bodies}} +{{if .IsSupportedByClient -}} +func (p *WebhookInitiator) {{$opid}}{{.Suffix}}(ctx context.Context, targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody, reqEditors... RequestEditorFn) (*http.Response, error) { + req, err := New{{$opid}}WebhookRequest{{.Suffix}}(targetURL{{if $hasParams}}, params{{end}}, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := p.applyWebhookEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return p.Client.Do(req) +} +{{end -}} +{{end}}{{/* range .Bodies */}} +{{end}}{{/* range . */}} + +{{/* Generate webhook request builders */}} +{{range . -}} +{{$hasParams := .RequiresParamObject -}} +{{$opid := .OperationId -}} +{{$method := .Method -}} +{{$webhookName := .WebhookName -}} + +{{range .Bodies}} +{{if .IsSupportedByClient -}} +// New{{$opid}}WebhookRequest{{.Suffix}} builds a {{.ContentType}} {{$method}} request for the {{$webhookName}} webhook +func New{{$opid}}WebhookRequest{{.Suffix}}(targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}, body {{$opid}}{{.NameTag}}RequestBody) (*http.Request, error) { + var bodyReader io.Reader + {{if .IsJSON -}} + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + {{else if eq .NameTag "Formdata" -}} + bodyStr, err := runtime.MarshalForm(body, nil) + if err != nil { + return nil, err + } + bodyReader = strings.NewReader(bodyStr.Encode()) + {{else if eq .NameTag "Text" -}} + if stringer, ok := interface{}(body).(fmt.Stringer); ok { + bodyReader = strings.NewReader(stringer.String()) + } else { + {{if .Schema.IsPrimitive -}} + bodyReader = strings.NewReader(fmt.Sprint(body)) + {{else -}} + return nil, fmt.Errorf("text/plain is not supported for complex types, define a String() method on {{.Schema.TypeDecl}} to marshal it as text") + {{end -}} + } + {{end -}} + return New{{$opid}}WebhookRequestWithBody(targetURL{{if $hasParams}}, params{{end}}, "{{.ContentType}}", bodyReader) +} +{{end -}} +{{end}} + +// New{{$opid}}WebhookRequest{{if .HasBody}}WithBody{{end}} builds a {{.Method}} request for the {{.WebhookName}} webhook{{if .HasBody}} with any body{{end}} +func New{{$opid}}WebhookRequest{{if .HasBody}}WithBody{{end}}(targetURL string{{if $hasParams}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}) (*http.Request, error) { + var err error + _ = err + + reqURL, err := url.Parse(targetURL) + if err != nil { + return nil, err + } + +{{if .QueryParams}} + if params != nil { + queryValues := reqURL.Query() + var rawQueryFragments []string + {{range $paramIdx, $param := .QueryParams}} + {{if .RequiresNilCheck}} if params.{{.GoName}} != nil { {{end}} + {{if .IsPassThrough}} + queryValues.Add("{{.ParamName}}", {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}) + {{end}} + {{if .IsJson}} + if queryParamBuf, err := json.Marshal({{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}); err != nil { + return nil, err + } else { + queryValues.Add("{{.ParamName}}", string(queryParamBuf)) + } + {{end}} + {{if .IsStyled}} + if queryFrag, err := runtime.StyleParamWithOptions("{{.Style}}", {{.Explode}}, "{{.ParamName}}", {{if and .RequiresNilCheck .HasOptionalPointer}}*{{end}}params.{{.GoName}}, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + {{end}} + {{if .RequiresNilCheck}}}{{end}} + {{end}} + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + reqURL.RawQuery = strings.Join(rawQueryFragments, "&") + } +{{end}}{{/* if .QueryParams */}} + + req, err := http.NewRequest({{.Method | httpMethodConstant}}, reqURL.String(), {{if .HasBody}}body{{else}}nil{{end}}) + if err != nil { + return nil, err + } + + {{if .HasBody}}req.Header.Add("Content-Type", contentType){{end}} +{{ if .HeaderParams }} + if params != nil { + {{range $paramIdx, $param := .HeaderParams}} + {{if .RequiresNilCheck}} if params.{{.GoName}} != nil { {{end}} + var headerParam{{$paramIdx}} string + {{if .IsPassThrough}} + headerParam{{$paramIdx}} = {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}} + {{end}} + {{if .IsJson}} + var headerParamBuf{{$paramIdx}} []byte + headerParamBuf{{$paramIdx}}, err = json.Marshal({{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}) + if err != nil { + return nil, err + } + headerParam{{$paramIdx}} = string(headerParamBuf{{$paramIdx}}) + {{end}} + {{if .IsStyled}} + headerParam{{$paramIdx}}, err = runtime.StyleParamWithOptions("{{.Style}}", {{.Explode}}, "{{.ParamName}}", {{if .HasOptionalPointer}}*{{end}}params.{{.GoName}}, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) + if err != nil { + return nil, err + } + {{end}} + req.Header.Set("{{.ParamName}}", headerParam{{$paramIdx}}) + {{if .RequiresNilCheck}}}{{end}} + {{end}} + } +{{- end }}{{/* if .HeaderParams */}} + return req, nil +} + +{{end}}{{/* Range */}} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/test_spec.yaml b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/test_spec.yaml index b50f7491c..f20cbecdc 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/test_spec.yaml +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/test_spec.yaml @@ -164,15 +164,45 @@ components: format: date-time CatDead: + required: + - sliced_birds + - very_dead_since + - very_memorable_birds properties: name: type: string + favourite_birds: + type: array + items: + type: string + nullable: true + detested_birds: + type: array + items: + type: string + sliced_birds: + type: array + items: + type: string + forgettable_birds: + additionalProperties: + type: string + nullable: true + memorable_birds: + additionalProperties: + type: string + very_memorable_birds: + additionalProperties: + type: string dead_since: type: string format: date-time x-oapi-codegen-extra-tags: tag1: value1 tag2: value2 + very_dead_since: + type: string + format: date-time cause: type: string enum: [ car, dog, oldage ] diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/typemapping.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/typemapping.go new file mode 100644 index 000000000..b47b63bf6 --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/typemapping.go @@ -0,0 +1,108 @@ +package codegen + +import "maps" + +// SimpleTypeSpec defines the Go type for an OpenAPI type/format combination, +// along with any import required to use it. +type SimpleTypeSpec struct { + Type string `yaml:"type" json:"type"` + Import string `yaml:"import,omitempty" json:"import,omitempty"` +} + +// FormatMapping defines the default Go type and format-specific overrides +// for an OpenAPI type. +type FormatMapping struct { + Default SimpleTypeSpec `yaml:"default" json:"default"` + Formats map[string]SimpleTypeSpec `yaml:"formats,omitempty" json:"formats,omitempty"` +} + +// TypeMapping defines the mapping from OpenAPI types to Go types. +type TypeMapping struct { + Integer FormatMapping `yaml:"integer,omitempty" json:"integer"` + Number FormatMapping `yaml:"number,omitempty" json:"number"` + Boolean FormatMapping `yaml:"boolean,omitempty" json:"boolean"` + String FormatMapping `yaml:"string,omitempty" json:"string"` +} + +// Merge returns a new TypeMapping with user overrides applied on top of base. +func (base TypeMapping) Merge(user TypeMapping) TypeMapping { + return TypeMapping{ + Integer: base.Integer.merge(user.Integer), + Number: base.Number.merge(user.Number), + Boolean: base.Boolean.merge(user.Boolean), + String: base.String.merge(user.String), + } +} + +func (base FormatMapping) merge(user FormatMapping) FormatMapping { + result := FormatMapping{ + Default: base.Default, + Formats: make(map[string]SimpleTypeSpec), + } + + // Copy base formats + maps.Copy(result.Formats, base.Formats) + + // Override with user default if specified + if user.Default.Type != "" { + result.Default = user.Default + } + + // Override/add user formats + maps.Copy(result.Formats, user.Formats) + + return result +} + +// Resolve returns the SimpleTypeSpec for a given format string. +// If the format has a specific mapping, that is returned; otherwise the default is used. +func (fm FormatMapping) Resolve(format string) SimpleTypeSpec { + if format != "" { + if spec, ok := fm.Formats[format]; ok { + return spec + } + } + return fm.Default +} + +// DefaultTypeMapping provides the default OpenAPI type/format to Go type mappings. +var DefaultTypeMapping = TypeMapping{ + Integer: FormatMapping{ + Default: SimpleTypeSpec{Type: "int"}, + Formats: map[string]SimpleTypeSpec{ + "int": {Type: "int"}, + "int8": {Type: "int8"}, + "int16": {Type: "int16"}, + "int32": {Type: "int32"}, + "int64": {Type: "int64"}, + "uint": {Type: "uint"}, + "uint8": {Type: "uint8"}, + "uint16": {Type: "uint16"}, + "uint32": {Type: "uint32"}, + "uint64": {Type: "uint64"}, + }, + }, + Number: FormatMapping{ + Default: SimpleTypeSpec{Type: "float32"}, + Formats: map[string]SimpleTypeSpec{ + "float": {Type: "float32"}, + "double": {Type: "float64"}, + }, + }, + Boolean: FormatMapping{ + Default: SimpleTypeSpec{Type: "bool"}, + }, + String: FormatMapping{ + Default: SimpleTypeSpec{Type: "string"}, + Formats: map[string]SimpleTypeSpec{ + "byte": {Type: "[]byte"}, + "email": {Type: "openapi_types.Email"}, + "date": {Type: "openapi_types.Date"}, + "date-time": {Type: "time.Time", Import: "time"}, + "duration": {Type: "openapi_types.Duration"}, + "json": {Type: "json.RawMessage", Import: "encoding/json"}, + "uuid": {Type: "openapi_types.UUID"}, + "binary": {Type: "openapi_types.File"}, + }, + }, +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/utils.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/utils.go index 5326e672b..55810bbb9 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/utils.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/utils.go @@ -15,12 +15,14 @@ package codegen import ( "bytes" + "cmp" "fmt" "go/token" + "maps" "net/url" "reflect" "regexp" - "sort" + "slices" "strconv" "strings" "unicode" @@ -81,7 +83,7 @@ func (m NameNormalizerMap) Options() []string { options = append(options, string(key)) } - sort.Strings(options) + slices.Sort(options) return options } @@ -205,7 +207,7 @@ func LowercaseFirstCharacters(str string) string { runes := []rune(str) - for i := 0; i < len(runes); i++ { + for i := range runes { next := i + 1 if i != 0 && next < len(runes) && unicode.IsLower(runes[next]) { break @@ -224,25 +226,25 @@ func LowercaseFirstCharacters(str string) string { func ToCamelCase(str string) string { s := strings.Trim(str, " ") - n := "" + var n strings.Builder capNext := true for _, v := range s { if unicode.IsUpper(v) { - n += string(v) + n.WriteString(string(v)) } if unicode.IsDigit(v) { - n += string(v) + n.WriteString(string(v)) } if unicode.IsLower(v) { if capNext { - n += strings.ToUpper(string(v)) + n.WriteString(strings.ToUpper(string(v))) } else { - n += string(v) + n.WriteString(string(v)) } } _, capNext = separatorSet[v] } - return n + return n.String() } // ToCamelCaseWithDigits function will convert query-arg style strings to CamelCase. We will @@ -349,7 +351,7 @@ func SortedMapKeys[T any](m map[string]T) []string { for k := range m { keys = append(keys, k) } - sort.Strings(keys) + slices.Sort(keys) return keys } @@ -371,11 +373,11 @@ func SortedSchemaKeys(dict map[string]*openapi3.SchemaRef) []string { } } - sort.Slice(keys, func(i, j int) bool { - if i, j := orders[keys[i]], orders[keys[j]]; i != j { - return i < j - } - return keys[i] < keys[j] + slices.SortFunc(keys, func(a, b string) int { + return cmp.Or( + cmp.Compare(orders[a], orders[b]), + cmp.Compare(a, b), + ) }) return keys } @@ -404,15 +406,11 @@ func schemaXOrder(v *openapi3.SchemaRef) (int64, bool) { return 0, false } -// StringInArray checks whether the specified string is present in an array -// of strings -func StringInArray(str string, array []string) bool { - for _, elt := range array { - if elt == str { - return true - } - } - return false +// SortedSecuritySchemeKeys returns sorted keys for a SecuritySchemeRef dict +func SortedSecuritySchemeKeys(dict map[string]*openapi3.SecuritySchemeRef) []string { + keys := slices.Collect(maps.Keys(dict)) + slices.Sort(keys) + return keys } // RefPathToObjName returns the name of referenced object without changes. @@ -478,6 +476,20 @@ func refPathToGoTypeSelf(refPath string, local bool) (string, error) { return "", fmt.Errorf("unexpected reference depth: %d for ref: %s local: %t", depth, refPath, local) } + // When multi-pass name resolution is active, the resolved name takes + // precedence over the spec-given name. For a $ref like + // #/components/schemas/Thing, we pass the section ("schemas") and + // name ("Thing") to resolvedNameForComponent, which looks up the + // final Go type name assigned by the collision resolver. + // Note: the resolver prioritizes component schemas — if a schema and + // a response both claim "Thing", the component schema keeps the original + // name and the response becomes "ThingResponse". + if depth == 4 && pathParts[0] == "#" && pathParts[1] == "components" { + if resolved := resolvedNameForComponent(pathParts[2], pathParts[3]); resolved != "" { + return resolved, nil + } + } + // Schemas may have been renamed locally, so look up the actual name in // the spec. name, err := findSchemaNameByRefPath(refPath, globalState.spec) @@ -535,6 +547,23 @@ func SwaggerUriToIrisUri(uri string) string { return pathParamRE.ReplaceAllString(uri, ":$1") } +// escapeLiteralPathColons escapes literal ':' characters in an OpenAPI path so +// that routers which use ':' to introduce a path parameter (Echo, Gin, Fiber) +// treat them as literals rather than parameter delimiters. Without this, a path +// like "/pets:validate" registers a parameter named "validate", so multiple +// such paths collide on the same prefix (issue #1726). +// +// The escaped form is a single backslash before the colon (`\:`). The register +// templates render the result through toGoString (strconv.Quote), which turns +// the backslash into `\\` in the emitted Go source, so the value the router +// sees at runtime is exactly `\:` — the escape those routers understand. +// +// It must run before parameter substitution, which introduces its own ':' +// delimiters that must stay unescaped. +func escapeLiteralPathColons(uri string) string { + return strings.ReplaceAll(uri, ":", `\:`) +} + // SwaggerUriToEchoUri converts a OpenAPI style path URI with parameters to an // Echo compatible path URI. We need to replace all of OpenAPI parameters with // ":param". Valid input parameters are: @@ -548,6 +577,7 @@ func SwaggerUriToIrisUri(uri string) string { // {?param} // {?param*} func SwaggerUriToEchoUri(uri string) string { + uri = escapeLiteralPathColons(uri) return pathParamRE.ReplaceAllString(uri, ":$1") } @@ -564,6 +594,7 @@ func SwaggerUriToEchoUri(uri string) string { // {?param} // {?param*} func SwaggerUriToFiberUri(uri string) string { + uri = escapeLiteralPathColons(uri) return pathParamRE.ReplaceAllString(uri, ":$1") } @@ -596,6 +627,7 @@ func SwaggerUriToChiUri(uri string) string { // {?param} // {?param*} func SwaggerUriToGinUri(uri string) string { + uri = escapeLiteralPathColons(uri) return pathParamRE.ReplaceAllString(uri, ":$1") } @@ -616,8 +648,9 @@ func SwaggerUriToGorillaUri(uri string) string { } // SwaggerUriToStdHttpUri converts a swagger style path URI with parameters to a -// Chi compatible path URI. We need to replace all Swagger parameters with -// "{param}". Valid input parameters are: +// net/http ServeMux compatible path URI. Parameter names are sanitized to be +// valid Go identifiers, as required by ServeMux wildcard segments. Valid input +// parameters are: // // {param} // {param*} @@ -628,13 +661,25 @@ func SwaggerUriToGorillaUri(uri string) string { // {?param} // {?param*} func SwaggerUriToStdHttpUri(uri string) string { - // https://pkg.go.dev/net/http#hdr-Patterns-ServeMux - // The special wildcard {$} matches only the end of the URL. For example, the pattern "/{$}" matches only the path "/", whereas the pattern "/" matches every path. - if uri == "/" { - return "/{$}" - } + uri = pathParamRE.ReplaceAllStringFunc(uri, func(match string) string { + sub := pathParamRE.FindStringSubmatch(match) + return "{" + SanitizeGoIdentifier(sub[1]) + "}" + }) - return pathParamRE.ReplaceAllString(uri, "{$1}") + // https://pkg.go.dev/net/http#hdr-Patterns-ServeMux + // A ServeMux pattern ending in '/' matches the whole subtree below it, + // while an OpenAPI path ending in '/' means exactly that path. The + // special wildcard {$} anchors the pattern to the end of the URL: + // "/foo/{$}" matches only "/foo/", whereas "/foo/" matches every path + // under it. Anchoring also prevents registration panics when subtree + // patterns from independent spec paths overlap ambiguously (#2065). + // Appended after parameter sanitization so the '$' is not treated as a + // parameter name. + if strings.HasSuffix(uri, "/") { + uri += "{$}" + } + + return uri } // OrderedParamsFromUri returns the argument names, in order, in a given URI string, so for @@ -654,15 +699,29 @@ func ReplacePathParamsWithStr(uri string) string { } // SortParamsByPath reorders the given parameter definitions to match those in the path URI. +// If a parameter appears more than once in the path (e.g. Keycloak's +// /clients/{client-uuid}/roles/{role-name}/composites/clients/{client-uuid}), +// duplicates are removed and only the first occurrence determines the order. func SortParamsByPath(path string, in []ParameterDefinition) ([]ParameterDefinition, error) { pathParams := OrderedParamsFromUri(path) + + // Deduplicate, preserving first-occurrence order. + seen := make(map[string]struct{}, len(pathParams)) + uniqueParams := make([]string, 0, len(pathParams)) + for _, name := range pathParams { + if _, exists := seen[name]; !exists { + seen[name] = struct{}{} + uniqueParams = append(uniqueParams, name) + } + } + n := len(in) - if len(pathParams) != n { + if len(uniqueParams) != n { return nil, fmt.Errorf("path '%s' has %d positional parameters, but spec has %d declared", - path, len(pathParams), n) + path, len(uniqueParams), n) } - out := make([]ParameterDefinition, len(in)) - for i, name := range pathParams { + out := make([]ParameterDefinition, n) + for i, name := range uniqueParams { p := ParameterDefinitions(in).FindByName(name) if p == nil { return nil, fmt.Errorf("path '%s' refers to parameter '%s', which doesn't exist in specification", @@ -719,9 +778,12 @@ func IsValidGoIdentity(str string) bool { return !IsPredeclaredGoIdentifier(str) } -// SanitizeGoIdentity deletes and replaces the illegal runes in the given -// string to use the string as a valid identity. -func SanitizeGoIdentity(str string) string { +// SanitizeGoIdentifier replaces illegal runes in the given string so that +// it is a valid Go identifier. Unlike SanitizeGoIdentity, it does not +// prefix reserved keywords or predeclared identifiers. This is useful for +// contexts where the name must be a valid identifier but is not used as a +// Go symbol (e.g. net/http ServeMux wildcard names). +func SanitizeGoIdentifier(str string) string { sanitized := []rune(str) for i, c := range sanitized { @@ -732,7 +794,14 @@ func SanitizeGoIdentity(str string) string { } } - str = string(sanitized) + return string(sanitized) +} + +// SanitizeGoIdentity deletes and replaces the illegal runes in the given +// string to use the string as a valid identity. It also prefixes reserved +// keywords and predeclared identifiers with an underscore. +func SanitizeGoIdentity(str string) string { + str = SanitizeGoIdentifier(str) if IsGoKeyword(str) || IsPredeclaredGoIdentifier(str) { str = "_" + str @@ -864,6 +933,15 @@ func PathToTypeName(path []string) string { return strings.Join(path, "_") } +// StringToGoString takes an arbitrary string and converts it to a valid Go string literal, +// including the quotes. For instance, `foo "bar"` would be converted to `"foo \"bar\""`. +// +// strconv.Quote escapes backslashes, newlines and other control characters too, +// so untrusted spec text cannot break out of the generated string literal. +func StringToGoString(in string) string { + return strconv.Quote(in) +} + // StringToGoComment renders a possible multi-line string as a valid Go-Comment. // Each line is prefixed as a comment. func StringToGoComment(in string) string { @@ -1047,13 +1125,30 @@ func findSchemaNameByRefPath(refPath string, spec *openapi3.T) (string, error) { } func ParseGoImportExtension(v *openapi3.SchemaRef) (*goImport, error) { - if v.Value.Extensions[extPropGoImport] == nil || v.Value.Extensions[extPropGoType] == nil { + // An x-go-type-import is only meaningful in concert with an x-go-type + // override. Without one, the imported package is never referenced in + // the generated code, producing an "imported and not used" compile + // error. Require at least one x-go-type to be in scope (either next to + // the $ref or on the referenced schema) before collecting an import. + hasGoType := v.Extensions[extPropGoType] != nil || + (v.Value != nil && v.Value.Extensions[extPropGoType] != nil) + if !hasGoType { return nil, nil } - goTypeImportExt := v.Value.Extensions[extPropGoImport] + var goTypeImportExt any - importI, ok := goTypeImportExt.(map[string]interface{}) + // check extensions next to the $ref before checking the schema itself + // values next to $ref will be used before those in the actual schema + if v.Extensions[extPropGoImport] != nil { + goTypeImportExt = v.Extensions[extPropGoImport] + } else if v.Value != nil && v.Value.Extensions[extPropGoImport] != nil { + goTypeImportExt = v.Value.Extensions[extPropGoImport] + } else { + return nil, nil + } + + importI, ok := goTypeImportExt.(map[string]any) if !ok { return nil, fmt.Errorf("failed to convert type: %T", goTypeImportExt) } @@ -1079,12 +1174,6 @@ func ParseGoImportExtension(v *openapi3.SchemaRef) (*goImport, error) { return &gi, nil } -func MergeImports(dst, src map[string]goImport) { - for k, v := range src { - dst[k] = v - } -} - // TypeDefinitionsEquivalent checks for equality between two type definitions, but // not every field is considered. We only want to know if they are fundamentally // the same type. @@ -1101,14 +1190,12 @@ func isAdditionalPropertiesExplicitFalse(s *openapi3.Schema) bool { return false } - return *s.AdditionalProperties.Has == false //nolint:staticcheck + return !*s.AdditionalProperties.Has } -func sliceContains[E comparable](s []E, v E) bool { - for _, ss := range s { - if ss == v { - return true - } - } - return false +// normalizeWhitespace converts `\n`, `\r` and `\r\n` to `\n`, and removes any trailing newline(s) +func normalizeWhitespace(s string) string { + s = strings.ReplaceAll(s, "\r\n", "\n") + s = strings.ReplaceAll(s, "\r", "\n") + return strings.TrimRight(s, "\n\r") } diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/validate_spec.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/validate_spec.go new file mode 100644 index 000000000..d7f31e46c --- /dev/null +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen/validate_spec.go @@ -0,0 +1,410 @@ +package codegen + +import ( + "errors" + "fmt" + "go/token" + "unicode" + + "github.com/getkin/kin-openapi/openapi3" +) + +// ValidateSpec walks a loaded OpenAPI document and rejects values that cannot be +// represented in the generated Go source. +// +// oapi-codegen copies many spec strings verbatim into the code it emits: +// parameter names, property names, media types and enum values become Go string +// literals and struct-tag contents; the `x-go-name`/`x-go-type`/`x-go-type-name` +// extensions become Go identifiers and type expressions. A value containing a +// quote, a backtick or a control character (such as a newline) would produce Go +// that does not compile. Catching that here turns an obscure compiler error on +// generated code into a clear message that points at the offending part of the +// spec. +// +// It is meant to run after any overlay has been applied and after tag/operation +// filtering and component pruning, so only values that will actually be emitted +// are checked. `$ref`s are not followed: every ref resolves to a component that +// is validated at its own definition, and the contents of external documents are +// never copied into the generated output, so they cannot affect it. +func ValidateSpec(spec *openapi3.T) error { + if spec == nil { + return nil + } + v := &specValidator{} + v.walkDocument(spec) + return errors.Join(v.errs...) +} + +type specValidator struct { + errs []error +} + +func (v *specValidator) addf(format string, args ...any) { + v.errs = append(v.errs, fmt.Errorf(format, args...)) +} + +// checkText validates a free-form string that is copied verbatim into the +// generated code (a name, media type, enum value, path, or tag content). Such a +// value ends up inside Go string literals, backtick-delimited struct tags and +// line comments, so it may not contain a double quote, a backtick, or a control +// character (newlines included). `what` describes the location for the message. +func (v *specValidator) checkText(value, what string) { + for _, r := range value { + if r == '"' || r == '`' || unicode.IsControl(r) { + v.addf("%s may not contain %s: %q", what, describeRune(r), value) + return + } + } +} + +// checkNoControl validates a string that is copied into the generated code but +// may legitimately contain quotes — for example a media type, whose parameters +// use quoted-strings (`application/ld+json; profile="…"`). Such values are +// emitted through strconv.Quote at their string-literal sinks, which handles the +// quotes safely, so only control characters (newlines that would break out of a +// line comment) are rejected here. +func (v *specValidator) checkNoControl(value, what string) { + for _, r := range value { + if unicode.IsControl(r) { + v.addf("%s may not contain %s: %q", what, describeRune(r), value) + return + } + } +} + +// checkIdentifier validates a value that becomes a Go identifier (the `x-go-name` +// and `x-go-type-name` extensions). It must be a valid, non-keyword Go +// identifier, which by definition excludes quotes, backticks and control +// characters. +func (v *specValidator) checkIdentifier(value, what string) { + if !token.IsIdentifier(value) { + v.addf("%s must be a valid Go identifier: %q", what, value) + } +} + +// checkGoType validates a value that becomes a Go type expression (the `x-go-type` +// extension). It legitimately contains characters an identifier cannot (`.`, +// `[]`, `*`, `map[...]`, `struct{}`, …), so instead of an identifier check it +// forbids the characters that would let the value escape the type position and +// introduce a new declaration or statement: quotes, backticks, semicolons and +// control characters. +func (v *specValidator) checkGoType(value, what string) { + for _, r := range value { + if r == '"' || r == '`' || r == ';' || unicode.IsControl(r) { + v.addf("%s must be a Go type expression and may not contain %s: %q", what, describeRune(r), value) + return + } + } +} + +// checkTagKey validates the key of an `x-oapi-codegen-extra-tags` entry. Struct +// tag keys are space-separated tokens terminated by ':', so they may not contain +// spaces, colons, quotes, backticks or control characters, and may not be empty. +func (v *specValidator) checkTagKey(key, what string) { + if key == "" { + v.addf("%s may not be empty", what) + return + } + for _, r := range key { + if r == '"' || r == '`' || r == ':' || r == ' ' || unicode.IsControl(r) { + v.addf("%s may not contain %s: %q", what, describeRune(r), key) + return + } + } +} + +// checkSecurity validates the scopes named in security requirements. The +// provider names are emitted only after identifier sanitization, but scopes are +// copied verbatim into a generated `[]string` literal (via toStringArray), so +// they are checked as text. +func (v *specValidator) checkSecurity(reqs openapi3.SecurityRequirements, where string) { + for _, req := range reqs { + for _, provider := range SortedMapKeys(req) { + for _, scope := range req[provider] { + v.checkText(scope, fmt.Sprintf("security scope for %q in %s", provider, where)) + } + } + } +} + +func describeRune(r rune) string { + switch r { + case '"': + return "a double quote" + case '`': + return "a backtick" + case '\n': + return "a newline" + case '\r': + return "a carriage return" + case '\t': + return "a tab" + case ';': + return "a semicolon" + case ':': + return "a colon" + case ' ': + return "a space" + } + if unicode.IsControl(r) { + return fmt.Sprintf("a control character (%U)", r) + } + return fmt.Sprintf("%q", r) +} + +// checkExtensions validates the code-affecting extensions on any spec object. +// Values are parsed with the same helpers code generation uses, and only checked +// when the parse succeeds — a malformed extension is ignored during generation, +// so it cannot inject anything and need not be rejected here. +func (v *specValidator) checkExtensions(ext map[string]any, where string) { + if ext == nil { + return + } + if raw, ok := ext[extGoName]; ok { + if s, err := extParseGoFieldName(raw); err == nil { + v.checkIdentifier(s, "x-go-name in "+where) + } + } + if raw, ok := ext[extGoTypeName]; ok { + if s, err := extTypeName(raw); err == nil { + v.checkIdentifier(s, "x-go-type-name in "+where) + } + } + if raw, ok := ext[extPropGoType]; ok { + if s, err := extString(raw); err == nil { + v.checkGoType(s, "x-go-type in "+where) + } + } + if raw, ok := ext[extPropExtraTags]; ok { + if tags, err := extExtraTags(raw); err == nil { + for _, k := range SortedMapKeys(tags) { + v.checkTagKey(k, "x-oapi-codegen-extra-tags key in "+where) + v.checkText(tags[k], fmt.Sprintf("x-oapi-codegen-extra-tags value for %q in %s", k, where)) + } + } + } +} + +func (v *specValidator) walkDocument(spec *openapi3.T) { + v.checkSecurity(spec.Security, "the root security requirements") + if spec.Paths != nil { + paths := spec.Paths.Map() + for _, path := range SortedMapKeys(paths) { + where := fmt.Sprintf("path %q", path) + v.checkText(path, "OpenAPI path") + v.walkPathItem(paths[path], where) + } + } + for _, name := range SortedMapKeys(spec.Webhooks) { + where := fmt.Sprintf("webhook %q", name) + v.checkText(name, "webhook name") + v.walkPathItem(spec.Webhooks[name], where) + } + if spec.Components != nil { + v.walkComponents(spec.Components) + } +} + +func (v *specValidator) walkComponents(c *openapi3.Components) { + for _, name := range SortedMapKeys(c.Schemas) { + v.walkSchemaRef(c.Schemas[name], fmt.Sprintf("schema %q", name)) + } + for _, name := range SortedMapKeys(c.Parameters) { + v.walkParameterRef(c.Parameters[name], fmt.Sprintf("component parameter %q", name)) + } + for _, name := range SortedMapKeys(c.RequestBodies) { + v.walkRequestBodyRef(c.RequestBodies[name], fmt.Sprintf("component request body %q", name)) + } + for _, name := range SortedMapKeys(c.Responses) { + v.walkResponseRef(c.Responses[name], fmt.Sprintf("component response %q", name)) + } + for _, name := range SortedMapKeys(c.Headers) { + v.walkHeaderRef(c.Headers[name], fmt.Sprintf("component header %q", name)) + } + for _, name := range SortedMapKeys(c.Callbacks) { + v.walkCallbackRef(c.Callbacks[name], fmt.Sprintf("component callback %q", name)) + } +} + +func (v *specValidator) walkPathItem(item *openapi3.PathItem, where string) { + if item == nil { + return + } + for _, p := range item.Parameters { + v.walkParameterRef(p, where) + } + for method, op := range item.Operations() { + v.walkOperation(op, fmt.Sprintf("%s %s", method, where)) + } +} + +func (v *specValidator) walkOperation(op *openapi3.Operation, where string) { + if op == nil { + return + } + v.checkExtensions(op.Extensions, where) + if op.Security != nil { + v.checkSecurity(*op.Security, where) + } + for _, p := range op.Parameters { + v.walkParameterRef(p, where) + } + v.walkRequestBodyRef(op.RequestBody, where) + if op.Responses != nil { + responses := op.Responses.Map() + for _, name := range SortedMapKeys(responses) { + v.walkResponseRef(responses[name], fmt.Sprintf("response %q in %s", name, where)) + } + } + for _, name := range SortedMapKeys(op.Callbacks) { + v.checkText(name, fmt.Sprintf("callback name in %s", where)) + v.walkCallbackRef(op.Callbacks[name], fmt.Sprintf("callback %q in %s", name, where)) + } +} + +func (v *specValidator) walkParameterRef(ref *openapi3.ParameterRef, where string) { + if ref == nil { + return + } + v.checkExtensions(ref.Extensions, where) + if ref.Ref != "" || ref.Value == nil { + return + } + p := ref.Value + loc := fmt.Sprintf("parameter %q in %s", p.Name, where) + v.checkText(p.Name, "parameter name in "+where) + v.checkText(p.Style, "style of "+loc) + v.checkExtensions(p.Extensions, loc) + v.walkContent(p.Content, loc) + v.walkSchemaRef(p.Schema, loc) +} + +func (v *specValidator) walkRequestBodyRef(ref *openapi3.RequestBodyRef, where string) { + if ref == nil { + return + } + v.checkExtensions(ref.Extensions, where) + if ref.Ref != "" || ref.Value == nil { + return + } + loc := "request body in " + where + v.checkExtensions(ref.Value.Extensions, loc) + v.walkContent(ref.Value.Content, loc) +} + +func (v *specValidator) walkResponseRef(ref *openapi3.ResponseRef, where string) { + if ref == nil { + return + } + v.checkExtensions(ref.Extensions, where) + if ref.Ref != "" || ref.Value == nil { + return + } + r := ref.Value + for _, name := range SortedMapKeys(r.Headers) { + v.checkText(name, "response header name in "+where) + v.walkHeaderRef(r.Headers[name], fmt.Sprintf("header %q in %s", name, where)) + } + v.walkContent(r.Content, where) + v.checkExtensions(r.Extensions, where) +} + +func (v *specValidator) walkHeaderRef(ref *openapi3.HeaderRef, where string) { + if ref == nil { + return + } + v.checkExtensions(ref.Extensions, where) + if ref.Ref != "" || ref.Value == nil { + return + } + v.checkExtensions(ref.Value.Extensions, where) + v.walkContent(ref.Value.Content, where) + v.walkSchemaRef(ref.Value.Schema, where) +} + +func (v *specValidator) walkCallbackRef(ref *openapi3.CallbackRef, where string) { + if ref == nil { + return + } + v.checkExtensions(ref.Extensions, where) + if ref.Ref != "" || ref.Value == nil { + return + } + items := ref.Value.Map() + for _, expr := range SortedMapKeys(items) { + v.walkPathItem(items[expr], where) + } +} + +func (v *specValidator) walkContent(content openapi3.Content, where string) { + for _, mediaType := range SortedMapKeys(content) { + v.checkNoControl(mediaType, "content type in "+where) + media := content[mediaType] + if media == nil { + continue + } + loc := fmt.Sprintf("content type %q in %s", mediaType, where) + v.checkExtensions(media.Extensions, loc) + v.walkSchemaRef(media.Schema, loc) + } +} + +func (v *specValidator) walkSchemaRef(ref *openapi3.SchemaRef, where string) { + if ref == nil { + return + } + // Extensions on the ref node itself are honoured by code generation even for + // a $ref — e.g. an `x-go-type` sibling next to a `$ref` overrides the + // referenced type — so they must be checked here. The referenced target is + // validated at its own definition, so we still do not follow the ref, which + // also keeps inline schemas a finite tree with no cycle tracking needed. + v.checkExtensions(ref.Extensions, where) + if ref.Ref != "" || ref.Value == nil { + return + } + v.walkSchema(ref.Value, where) +} + +func (v *specValidator) walkSchema(s *openapi3.Schema, where string) { + if s == nil { + return + } + v.checkExtensions(s.Extensions, where) + v.checkText(s.Format, "format in "+where) + if s.Type != nil { + for _, t := range s.Type.Slice() { + v.checkText(t, "type in "+where) + } + } + // Enum values are emitted through strconv.Quote in the const block, so quotes + // in a value are handled safely; only control characters are rejected. + for _, enumValue := range s.Enum { + if str, ok := enumValue.(string); ok { + v.checkNoControl(str, "enum value in "+where) + } + } + if s.Discriminator != nil { + v.checkText(s.Discriminator.PropertyName, "discriminator propertyName in "+where) + for _, key := range SortedMapKeys(s.Discriminator.Mapping) { + v.checkText(key, "discriminator mapping key in "+where) + } + } + for _, name := range SortedMapKeys(s.Properties) { + v.checkText(name, "property name in "+where) + v.walkSchemaRef(s.Properties[name], fmt.Sprintf("property %q in %s", name, where)) + } + v.walkSchemaRef(s.Items, "items in "+where) + if s.AdditionalProperties.Schema != nil { + v.walkSchemaRef(s.AdditionalProperties.Schema, "additionalProperties in "+where) + } + for i, sub := range s.AllOf { + v.walkSchemaRef(sub, fmt.Sprintf("allOf[%d] in %s", i, where)) + } + for i, sub := range s.AnyOf { + v.walkSchemaRef(sub, fmt.Sprintf("anyOf[%d] in %s", i, where)) + } + for i, sub := range s.OneOf { + v.walkSchemaRef(sub, fmt.Sprintf("oneOf[%d] in %s", i, where)) + } + v.walkSchemaRef(s.Not, "not in "+where) +} diff --git a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/util/loader.go b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/util/loader.go index 89830a8c4..982b4b06d 100644 --- a/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/util/loader.go +++ b/vendor/github.com/oapi-codegen/oapi-codegen/v2/pkg/util/loader.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/getkin/kin-openapi/openapi3" - "github.com/speakeasy-api/openapi-overlay/pkg/loader" + "github.com/speakeasy-api/openapi/overlay/loader" "gopkg.in/yaml.v3" ) @@ -16,6 +16,9 @@ func LoadSwagger(filePath string) (swagger *openapi3.T, err error) { loader := openapi3.NewLoader() loader.IsExternalRefsAllowed = true + // Record each element's source location so route registration can be + // emitted in the order paths are declared in the spec (issue #1887). + loader.IncludeOrigin = true u, err := url.Parse(filePath) if err == nil && u.Scheme != "" && u.Host != "" { @@ -48,13 +51,17 @@ func LoadSwaggerWithOverlay(filePath string, opts LoadSwaggerWithOverlayOpts) (s } // parse out the yaml.Node, which is required by the overlay library - data, err := yaml.Marshal(spec) + buf := &bytes.Buffer{} + enc := yaml.NewEncoder(buf) + // set to 2 to work around https://github.com/yaml/go-yaml/issues/76 + enc.SetIndent(2) + err = enc.Encode(spec) if err != nil { return nil, fmt.Errorf("failed to marshal spec from %#v as YAML: %w", filePath, err) } var node yaml.Node - err = yaml.NewDecoder(bytes.NewReader(data)).Decode(&node) + err = yaml.NewDecoder(buf).Decode(&node) if err != nil { return nil, fmt.Errorf("failed to parse spec from %#v: %w", filePath, err) } @@ -70,7 +77,7 @@ func LoadSwaggerWithOverlay(filePath string, opts LoadSwaggerWithOverlayOpts) (s } if opts.Strict { - err, vs := overlay.ApplyToStrict(&node) + vs, err := overlay.ApplyToStrict(&node) if err != nil { return nil, fmt.Errorf("failed to apply Overlay %#v to specification %#v: %v\nAdditionally, the following validation errors were found:\n- %s", opts.Path, filePath, err, strings.Join(vs, "\n- ")) } @@ -88,6 +95,7 @@ func LoadSwaggerWithOverlay(filePath string, opts LoadSwaggerWithOverlayOpts) (s loader := openapi3.NewLoader() loader.IsExternalRefsAllowed = true + loader.IncludeOrigin = true swagger, err = loader.LoadFromDataWithPath(b, &url.URL{ Path: filepath.ToSlash(filePath), diff --git a/vendor/github.com/oapi-codegen/runtime/Makefile b/vendor/github.com/oapi-codegen/runtime/Makefile index e83b8bc8c..feadfd0e4 100644 --- a/vendor/github.com/oapi-codegen/runtime/Makefile +++ b/vendor/github.com/oapi-codegen/runtime/Makefile @@ -1,32 +1,44 @@ GOBASE=$(shell pwd) GOBIN=$(GOBASE)/bin -help: - @echo "This is a helper makefile for oapi-codegen" - @echo "Targets:" - @echo " generate: regenerate all generated files" - @echo " test: run all tests" - @echo " gin_example generate gin example server code" - @echo " tidy tidy go mod" - $(GOBIN)/golangci-lint: - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v1.55.2 + curl -sSfL https://golangci-lint.run/install.sh | sh -s -- -b $(GOBIN) v2.12.2 .PHONY: tools tools: $(GOBIN)/golangci-lint lint: tools + # run the root module explicitly, to prevent recursive calls by re-invoking `make ...` top-level $(GOBIN)/golangci-lint run ./... + # then, for all child modules, use a module-managed `Makefile` + git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && env GOBIN=$(GOBIN) make lint' lint-ci: tools - $(GOBIN)/golangci-lint run ./... --out-format=github-actions --timeout=5m + # for the root module, explicitly run the step, to prevent recursive calls + $(GOBIN)/golangci-lint run ./... --output.text.path=stdout --timeout=5m + # then, for all child modules, use a module-managed `Makefile` + git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && env GOBIN=$(GOBIN) make lint-ci' generate: + # for the root module, explicitly run the step, to prevent recursive calls go generate ./... + # then, for all child modules, use a module-managed `Makefile` + git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && make generate' test: + # for the root module, explicitly run the step, to prevent recursive calls go test -cover ./... + # then, for all child modules, use a module-managed `Makefile` + git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && make test' tidy: - @echo "tidy..." + # for the root module, explicitly run the step, to prevent recursive calls go mod tidy + # then, for all child modules, use a module-managed `Makefile` + git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && make tidy' + +tidy-ci: + # for the root module, explicitly run the step, to prevent recursive calls + tidied -verbose + # then, for all child modules, use a module-managed `Makefile` + git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && make tidy-ci' diff --git a/vendor/github.com/oapi-codegen/runtime/README.md b/vendor/github.com/oapi-codegen/runtime/README.md index 7392812eb..82b864966 100644 --- a/vendor/github.com/oapi-codegen/runtime/README.md +++ b/vendor/github.com/oapi-codegen/runtime/README.md @@ -3,4 +3,221 @@ ⚠️ This README may be for the latest development version, which may contain unreleased changes. Please ensure you're looking at the README for the latest release version. -This provides any runtime-specific code that the generated code that oapi-codegen generates may need, and therefore is expected to be used with [deepmap/oapi-codegen](https://github.com/deepmap/oapi-codegen). +This package provides helper functions for marshaling and unmarshalling HTTP +parameters in headers, cookies, and query arguments in various formats, as well +as functions for reading and writing form encoded data representing models. + +You won't need to use this package directly, since it's imported be the boilerplate +from `oapi-codegen`, however, you do need to use the correct version needed by +the code generator, since it makes assumptions about runtime behavior. + +## Parameter Handling + +OpenAPI 3.x parameters are characterized by three orthogonal attributes: +**style**, **location**, and **explode**. The serialized form on the wire is +determined by the combination of all three. + +### Styles + +Parameters come in the following styles (all defined by the OpenAPI 3.x spec): + +- **`simple`** — comma-separated values. The default for path and header + parameters. +- **`label`** — values prefixed with `.`, separated by `.` (explode) or `,` + (no explode). Path parameters only. +- **`matrix`** — values prefixed with `;name=`, repeated (explode) or + comma-separated (no explode). Path parameters only. +- **`form`** — `name=value` pairs joined with `&`. The default for query and + cookie parameters. +- **`spaceDelimited`** — array elements joined by literal spaces (no + explode); behaves identically to `form` when exploded. Query parameters, + arrays only. +- **`pipeDelimited`** — array elements joined by literal `|` (no explode); + behaves identically to `form` when exploded. Query parameters, arrays + only. +- **`deepObject`** — nested bracket notation, e.g. `name[field]=value`. + Query parameters, objects only, must be exploded. + +### Locations + +Each style is only valid in specific parameter locations: + +| Location | Allowed styles | +|----------|---------------| +| `path` | `simple`, `label`, `matrix` | +| `query` | `form`, `spaceDelimited`, `pipeDelimited`, `deepObject` | +| `header` | `simple` | +| `cookie` | `form` | + +### Explode + +Each style can be `explode: true` or `explode: false`, which changes how +multi-value parameters (arrays and objects) are packed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StyleTypeexplode: falseexplode: true
simpleprimitive 555
array [3,4,5]3,4,53,4,5
object {role:"admin", firstName:"Alex"}firstName,Alex,role,adminfirstName=Alex,role=admin
labelprimitive 5.5.5
array [3,4,5].3,4,5.3.4.5
object {role:"admin", firstName:"Alex"}.firstName,Alex,role,admin.firstName=Alex.role=admin
matrixprimitive 5;id=5;id=5
array [3,4,5];id=3,4,5;id=3;id=4;id=5
object {role:"admin", firstName:"Alex"};id=firstName,Alex,role,admin;firstName=Alex;role=admin
formprimitive 5id=5id=5
array [3,4,5]id=3,4,5id=3&id=4&id=5
object {role:"admin", firstName:"Alex"}id=firstName,Alex,role,adminfirstName=Alex&role=admin
spaceDelimitedprimitivenot supported
array [3,4,5]id=3 4 5id=3&id=4&id=5
objectnot supported
pipeDelimitedprimitivenot supported
array [3,4,5]id=3|4|5id=3&id=4&id=5
objectnot supported
deepObjectprimitivenot supported
array [3,4,5]id[0]=3&id[1]=4&id[2]=5 (explode required)
object {role:"admin", firstName:"Alex"}id[firstName]=Alex&id[role]=admin (explode required)
+ +> The above outputs are shown unescaped for readability. In real use, values +> destined for query parameters are passed through `url.QueryEscape` (or +> `url.PathEscape` for path parameters), so reserved characters and +> non-ASCII bytes are percent-encoded on the wire. + +### Parameter Limitations + +The OpenAPI 3.x parameter styles are convenient but each has at least one +sharp edge. The list below documents behaviors that surprise users and the +cases where round-tripping is not possible in principle. + +#### Encoding + +##### Configuring query encoding + +By default, spaces in query strings are encoded as `+`, following Go's +`net/url` (the form-urlencoded convention used by browsers). This is accepted +by most servers, but it is *not* RFC 3986 compliant — some servers (for +example, OData endpoints expecting `?filter=name%20eq%20'x'`) reject `+` for a +space with `400 Bad Request`. + +Generated clients route all query escaping through the package-level +`DefaultQueryEncoder`, so you can control this behavior. Two encoders ship out +of the box: + +- `NetURLQueryEncoder` — the default; encodes a space as `+`. +- `RFC3986QueryEncoder` — encodes a space as `%20` for strict RFC 3986 + compliance. + +To opt into RFC 3986 encoding, set the default once during program +initialization (it is not safe to mutate concurrently with in-flight requests, +the same contract as `http.DefaultClient`): + +```go +func init() { + runtime.DefaultQueryEncoder = runtime.RFC3986QueryEncoder{} +} +``` + +You may also supply your own `QueryEncoder` implementation (a single +`EscapeQueryValue` method). The encoder governs query parameter values, names, +and map keys (including `deepObject`); it does **not** affect path escaping or +`application/x-www-form-urlencoded` request bodies, where `+` for a space is the +correct, media-type-defined behavior. + +- **Query and path values are percent-encoded.** Reserved characters + (`&`, `=`, `#`, `?`, etc.) and non-ASCII bytes are escaped via + `url.QueryEscape` / `url.PathEscape`. Spaces in query values are encoded + as `+` (form-urlencoded convention), matching `url.Values.Encode()`, + unless `DefaultQueryEncoder` is changed (see above). +- **Header values are passed through raw.** Per RFC 7230 §3.2.6, header + field values may contain visible ASCII plus space/tab; bytes ≥ `0x80` are + `obs-text` and explicitly marked obsolete in RFC 9110. There is no + generally-agreed mechanism for transporting non-ASCII text in arbitrary + header values (RFC 8187 covers only header *parameters* like + `Content-Disposition` `filename*=`). If your spec puts non-ASCII or + control characters into a header parameter, the wire format is + RFC-noncompliant and proxies may strip or reject the request. +- **Cookie values are passed through raw.** Per RFC 6265 §4.1.1, cookie + values may not contain `CTL`, whitespace, `"`, `,`, `;`, `\`, or any byte + ≥ `0x80`. Most cookie libraries URL-encode by convention, but this + package does not — if your spec puts spaces or non-ASCII into a cookie + parameter, the value will not be RFC 6265-conformant. +- **Map keys are percent-encoded for `deepObject` only.** For `simple`, + `label`, `matrix`, and `form` styles, map keys are emitted raw. If your + map keys are non-ASCII or contain URL-reserved characters, the wire + representation will not be encoded. +- **`allowReserved`** (`StyleParamOptions.AllowReserved`) is a query-only + option per the OpenAPI 3.x spec, and only applies to *values*, not + parameter names or map keys. + +#### `deepObject` + +- **Bracket notation is structural, not data.** `MarshalDeepObject` + percent-encodes literal `[` and `]` inside values and map keys as `%5B` + / `%5D` on the wire. However, once a server calls `url.ParseQuery`, those + escapes are decoded back to `[` and `]` — at which point a key like + `p[a[b]]` is ambiguous between `{p: {a: {b: ...}}}` and + `{p: {"a[b]": ...}}`. `UnmarshalDeepObject` cannot distinguish these + cases and adopts the same greedy left-to-right parse used by qs (Node), + Rails `Rack::Utils.parse_nested_query`, and similar libraries: every + unescaped `[` opens a new nesting level. **Literal `[` and `]` inside + map keys cannot be round-tripped.** Use a different separator in + user-supplied keys if this matters to you. +- **OpenAPI 3.x defines `deepObject` only for object schemas.** This + package extends it to maps and arrays for convenience (arrays are + numerically indexed: `p[0]`, `p[1]`, …), but other tooling may not + accept that wire form. +- **`deepObject` requires `explode: true`.** Non-exploded `deepObject` has + no well-defined wire format; an error is returned. + +#### `spaceDelimited` / `pipeDelimited` + +- **Array-only.** Per the OpenAPI spec, these styles only apply to arrays + of primitives. Passing a primitive or object returns an error. +- **Exploded form degenerates to `form`.** When `explode: true`, the + separator becomes `&` (not space or pipe), so the output is + byte-identical to `form` exploded. The space/pipe separator only takes + effect when `explode: false`. This is per the OpenAPI spec, but it + surprises many users. +- **Unexploded `spaceDelimited` is RFC-fragile.** Literal spaces in a + query string are tolerated by most parsers but are not RFC 3986- + conformant; `+` would be the form-urlencoded canonical form for space, + but `spaceDelimited` is defined to use literal `%20`-equivalent space + (the value bytes themselves are then encoded normally). + +#### Type-conversion edge cases + +- **`null`** in a struct field marshals to the literal string `"null"` in + `deepObject` output. There is no distinct OpenAPI representation for + absent vs. JSON-null in query parameters. +- **`time.Time` and `types.Date`** are formatted via RFC 3339 and + `2006-01-02` respectively when used as primitives in any style. If you + want a different format, wrap the field in a type that implements + `encoding.TextMarshaler`. +- **`types.UUID`** stringifies to the canonical hyphenated 36-character + form; query/path location escaping is a no-op (UUIDs are in the + unreserved set). +- **`json.Marshaler` is consulted for structs**, then the result is + re-decoded with `UseNumber()` and re-styled. Numbers therefore retain + their original precision, but the round-trip through JSON means struct + field tags are honored (not raw Go field names). + diff --git a/vendor/github.com/oapi-codegen/runtime/bindform.go b/vendor/github.com/oapi-codegen/runtime/bindform.go index 31b19d97f..d7f3666ee 100644 --- a/vendor/github.com/oapi-codegen/runtime/bindform.go +++ b/vendor/github.com/oapi-codegen/runtime/bindform.go @@ -13,9 +13,23 @@ import ( "github.com/oapi-codegen/runtime/types" ) -const tagName = "json" +const jsonTagName = "json" +const formTagName = "form" const jsonContentType = "application/json" +// formFieldTag returns the struct tag value that names a field on the wire +// for form encoding. The `form` tag is preferred when present and non-empty; +// the `json` tag is the historical fallback, kept for compatibility with +// generated and hand-written structs that carry only json tags. The full tag +// value is returned, so `-` skips and the `,omitempty` option follow +// whichever tag was selected. +func formFieldTag(f reflect.StructField) string { + if tag, ok := f.Tag.Lookup(formTagName); ok && tag != "" { + return tag + } + return f.Tag.Get(jsonTagName) +} + type RequestBodyEncoding struct { ContentType string Style string @@ -41,7 +55,7 @@ func BindForm(ptr interface{}, form map[string][]string, files map[string][]*mul for i := 0; i < tValue.NumField(); i++ { field := ptrVal.Field(i) - tag := tValue.Field(i).Tag.Get(tagName) + tag := formFieldTag(tValue.Field(i)) if !field.CanInterface() || tag == "-" { continue } @@ -97,7 +111,7 @@ func MarshalForm(ptr interface{}, encodings map[string]RequestBodyEncoding) (url result := make(url.Values) for i := 0; i < tValue.NumField(); i++ { field := ptrVal.Field(i) - tag := tValue.Field(i).Tag.Get(tagName) + tag := formFieldTag(tValue.Field(i)) if !field.CanInterface() || tag == "-" { continue } @@ -127,7 +141,7 @@ func bindFormImpl(v reflect.Value, form map[string][]string, files map[string][] switch v.Kind() { case reflect.Interface: return bindFormImpl(v.Elem(), form, files, name) - case reflect.Ptr: + case reflect.Pointer: ptrData := v.Elem() if !ptrData.IsValid() { ptrData = reflect.New(v.Type().Elem()) @@ -175,7 +189,7 @@ func bindFormImpl(v reflect.Value, form map[string][]string, files map[string][] } for i := 0; i < v.NumField(); i++ { field := v.Type().Field(i) - tag := field.Tag.Get(tagName) + tag := formFieldTag(field) if field.Name == "AdditionalProperties" && field.Type.Kind() == reflect.Map && tag == "-" { additionalPropertiesHasData, err := bindAdditionalProperties(v.Field(i), v, form, files, name) if err != nil { @@ -194,6 +208,26 @@ func bindFormImpl(v reflect.Value, form map[string][]string, files map[string][] hasData = hasData || fieldHasData } return hasData, nil + case reflect.Map: + // A bool-keyed map (such as nullable.Nullable[T], which is + // map[bool]T) is treated as a nullable wrapper: bind the inner type + // and store the result under map[true]. An absent field stays as + // the zero (unspecified) map. + if v.Type().Key().Kind() == reflect.Bool { + valuePtr := reflect.New(v.Type().Elem()) + valueHasData, err := bindFormImpl(valuePtr.Elem(), form, files, name) + if err != nil { + return false, err + } + if valueHasData { + newMap := reflect.MakeMap(v.Type()) + newMap.SetMapIndex(reflect.ValueOf(true), valuePtr.Elem()) + v.Set(newMap) + return true, nil + } + return false, nil + } + return bindFormMap(v, form, files, name) default: value := form[name] if len(value) != 0 { @@ -238,7 +272,7 @@ func bindAdditionalProperties(additionalProperties reflect.Value, parentStruct r // store all fixed properties in a set fieldsSet := make(map[string]struct{}) for i := 0; i < parentStruct.NumField(); i++ { - tag := parentStruct.Type().Field(i).Tag.Get(tagName) + tag := formFieldTag(parentStruct.Type().Field(i)) if !parentStruct.Field(i).CanInterface() || tag == "-" { continue } @@ -286,9 +320,74 @@ func bindAdditionalProperties(additionalProperties reflect.Value, parentStruct r return hasData, nil } +// bindFormMap binds form (and file) entries of the form `name[key]=value` +// into a generic map[K]V destination. It is reached from bindFormImpl for +// non-bool-keyed maps; the bool-keyed case is handled separately as a +// nullable wrapper. +func bindFormMap(v reflect.Value, form map[string][]string, files map[string][]*multipart.FileHeader, name string) (bool, error) { + keyType := v.Type().Key() + valueType := v.Type().Elem() + result := reflect.MakeMap(v.Type()) + hasData := false + prefix := name + "[" + seen := map[string]struct{}{} + + process := func(formKey string) error { + if !strings.HasPrefix(formKey, prefix) { + return nil + } + inner := strings.TrimPrefix(formKey, prefix) + end := strings.Index(inner, "]") + if end < 0 { + return nil + } + innerKey := inner[:end] + if _, ok := seen[innerKey]; ok { + return nil + } + seen[innerKey] = struct{}{} + + keyPtr := reflect.New(keyType) + if err := BindStringToObject(innerKey, keyPtr.Interface()); err != nil { + return err + } + valuePtr := reflect.New(valueType) + innerHasData, err := bindFormImpl(valuePtr.Elem(), form, files, fmt.Sprintf("%s[%s]", name, innerKey)) + if err != nil { + return err + } + if innerHasData { + result.SetMapIndex(keyPtr.Elem(), valuePtr.Elem()) + hasData = true + } + return nil + } + + for k := range form { + if err := process(k); err != nil { + return false, err + } + } + for k := range files { + if err := process(k); err != nil { + return false, err + } + } + + if hasData { + v.Set(result) + } + return hasData, nil +} + func marshalFormImpl(v reflect.Value, result url.Values, name string) { switch v.Kind() { - case reflect.Interface, reflect.Ptr: + case reflect.Pointer: + if v.IsNil() { + break + } + fallthrough + case reflect.Interface: marshalFormImpl(v.Elem(), result, name) case reflect.Slice: for i := 0; i < v.Len(); i++ { @@ -298,7 +397,7 @@ func marshalFormImpl(v reflect.Value, result url.Values, name string) { case reflect.Struct: for i := 0; i < v.NumField(); i++ { field := v.Type().Field(i) - tag := field.Tag.Get(tagName) + tag := formFieldTag(field) if field.Name == "AdditionalProperties" && tag == "-" { iter := v.MapRange() for iter.Next() { diff --git a/vendor/github.com/oapi-codegen/runtime/bindparam.go b/vendor/github.com/oapi-codegen/runtime/bindparam.go index df069c9d3..10e0aa641 100644 --- a/vendor/github.com/oapi-codegen/runtime/bindparam.go +++ b/vendor/github.com/oapi-codegen/runtime/bindparam.go @@ -26,6 +26,17 @@ import ( "github.com/oapi-codegen/runtime/types" ) +// RequiredParameterError is returned when a required query parameter is missing. +// Generated server code can use errors.As to detect this and produce a +// framework-specific typed error for the application's error handler. +type RequiredParameterError struct { + ParamName string +} + +func (e *RequiredParameterError) Error() string { + return fmt.Sprintf("query parameter '%s' is required", e.ParamName) +} + // BindStyledParameter binds a parameter as described in the Path Parameters // section here to a Go object: // https://swagger.io/docs/specification/serialization/ @@ -63,6 +74,25 @@ type BindStyledParameterOptions struct { Explode bool // Whether the parameter is required in the query Required bool + // Type is the OpenAPI type of the parameter (e.g. "string", "integer"). + Type string + // Format is the OpenAPI format of the parameter (e.g. "byte", "date-time"). + // When set to "byte" and the destination is []byte, the value is + // base64-decoded rather than treated as a generic slice. + Format string + // AllowReserved, when true, indicates that the parameter value may + // contain RFC 3986 reserved characters without percent-encoding. + AllowReserved bool + // ValueIsUnescaped, when true, indicates that the value has already had + // its URL percent-encoding removed (typically by the router) and is + // bound verbatim. The default treats the value as still escaped and + // unescapes it according to ParamLocation — the historical behavior, + // preserved for existing callers. Routers differ in whether they + // deliver path parameters raw or already decoded — a mismatch either + // double-decodes values containing literal percent signs or leaves + // them encoded — so the generated wrapper sets this to match its + // framework. See https://github.com/oapi-codegen/runtime/issues/35 + ValueIsUnescaped bool } // BindStyledParameterWithOptions binds a parameter as described in the Path Parameters @@ -75,23 +105,26 @@ func BindStyledParameterWithOptions(style string, paramName string, value string } } - // Based on the location of the parameter, we need to unescape it properly. - var err error - switch opts.ParamLocation { - case ParamLocationQuery, ParamLocationUndefined: - // We unescape undefined parameter locations here for older generated code, - // since prior to this refactoring, they always query unescaped. - value, err = url.QueryUnescape(value) - if err != nil { - return fmt.Errorf("error unescaping query parameter '%s': %w", paramName, err) - } - case ParamLocationPath: - value, err = url.PathUnescape(value) - if err != nil { - return fmt.Errorf("error unescaping path parameter '%s': %w", paramName, err) + // Unless the caller says the router already unescaped the value, it is + // unescaped here according to the location of the parameter. Undefined + // locations are query-unescaped for older generated code, which always + // bound query-unescaped values. + if !opts.ValueIsUnescaped { + var err error + switch opts.ParamLocation { + case ParamLocationQuery, ParamLocationUndefined: + value, err = url.QueryUnescape(value) + if err != nil { + return fmt.Errorf("error unescaping query parameter '%s': %w", paramName, err) + } + case ParamLocationPath: + value, err = url.PathUnescape(value) + if err != nil { + return fmt.Errorf("error unescaping path parameter '%s': %w", paramName, err) + } + default: + // Headers and cookies aren't escaped. } - default: - // Headers and cookies aren't escaped. } // If the destination implements encoding.TextUnmarshaler we use it for binding @@ -109,7 +142,7 @@ func BindStyledParameterWithOptions(style string, paramName string, value string // This is the basic type of the destination object. t := v.Type() - if t.Kind() == reflect.Struct { + if t.Kind() == reflect.Struct || t.Kind() == reflect.Map { // We've got a destination object, we'll create a JSON representation // of the input value, and let the json library deal with the unmarshaling parts, err := splitStyledParameter(style, opts.Explode, true, paramName, value) @@ -121,6 +154,22 @@ func BindStyledParameterWithOptions(style string, paramName string, value string } if t.Kind() == reflect.Slice { + if opts.Format == "byte" && isByteSlice(t) { + parts, err := splitStyledParameter(style, opts.Explode, false, paramName, value) + if err != nil { + return fmt.Errorf("error splitting input '%s' into parts: %w", value, err) + } + if len(parts) != 1 { + return fmt.Errorf("expected single base64 value for byte slice parameter '%s', got %d parts", paramName, len(parts)) + } + decoded, err := base64Decode(parts[0]) + if err != nil { + return fmt.Errorf("error decoding base64 parameter '%s': %w", paramName, err) + } + v.SetBytes(decoded) + return nil + } + // Chop up the parameter into parts based on its style parts, err := splitStyledParameter(style, opts.Explode, false, paramName, value) if err != nil { @@ -130,7 +179,20 @@ func BindStyledParameterWithOptions(style string, paramName string, value string return bindSplitPartsToDestinationArray(parts, dest) } - // Try to bind the remaining types as a base type. + // For primitive types, only label and matrix styles need prefix stripping + // via splitStyledParameter. Simple and form styles can bind the raw value + // directly — splitting on commas would incorrectly reject primitive values + // that contain commas (see https://github.com/oapi-codegen/runtime/issues/114). + if style == "label" || style == "matrix" { + parts, err := splitStyledParameter(style, opts.Explode, false, paramName, value) + if err != nil { + return fmt.Errorf("error splitting parameter '%s': %w", paramName, err) + } + if len(parts) != 1 { + return fmt.Errorf("parameter '%s': expected single value, got %d parts", paramName, len(parts)) + } + value = parts[0] + } return BindStringToObject(value, dest) } @@ -306,8 +368,32 @@ func bindSplitPartsToDestinationStruct(paramName string, parts []string, explode // tell them apart. This code tries to fail, but the moral of the story is that // you shouldn't pass objects via form styled query arguments, just use // the Content parameter form. +// +// Deprecated: BindQueryParameter pre-decodes the query string via url.Values, +// which makes it impossible to distinguish literal commas from delimiter commas +// in form/explode=false parameters. Use BindRawQueryParameter instead, which +// operates on the raw query string and handles encoded delimiters correctly. func BindQueryParameter(style string, explode bool, required bool, paramName string, queryParams url.Values, dest interface{}) error { + return BindQueryParameterWithOptions(style, explode, required, paramName, queryParams, dest, BindQueryParameterOptions{}) +} + +// BindQueryParameterOptions defines optional arguments for BindQueryParameterWithOptions. +type BindQueryParameterOptions struct { + // Type is the OpenAPI type of the parameter (e.g. "string", "integer"). + Type string + // Format is the OpenAPI format of the parameter (e.g. "byte", "date-time"). + // When set to "byte" and the destination is []byte, the value is + // base64-decoded rather than treated as a generic slice. + Format string + // AllowReserved, when true, indicates that the parameter value may + // contain RFC 3986 reserved characters without percent-encoding. + AllowReserved bool +} + +// BindQueryParameterWithOptions works like BindQueryParameter with additional options. +func BindQueryParameterWithOptions(style string, explode bool, required bool, paramName string, + queryParams url.Values, dest interface{}, opts BindQueryParameterOptions) error { // dv = destination value. dv := reflect.Indirect(reflect.ValueOf(dest)) @@ -355,13 +441,17 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str k := t.Kind() switch style { - case "form": + case "form", "spaceDelimited", "pipeDelimited": var parts []string if explode { // ok, the explode case in query arguments is very, very annoying, // because an exploded object, such as /users?role=admin&firstName=Alex // isn't actually present in the parameter array. We have to do // different things based on destination type. + // + // Note: spaceDelimited and pipeDelimited with explode=true are + // serialized identically to form explode=true (each value is a + // separate key=value pair), so we share this code path. values, found := queryParams[paramName] var err error @@ -372,13 +462,24 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str if !found { if required { - return fmt.Errorf("query parameter '%s' is required", paramName) + return &RequiredParameterError{ParamName: paramName} } else { // If an optional parameter is not found, we do nothing, return nil } } - err = bindSplitPartsToDestinationArray(values, output) + if opts.Format == "byte" && isByteSlice(t) { + if len(values) != 1 { + return fmt.Errorf("expected single base64 value for byte slice parameter '%s', got %d values", paramName, len(values)) + } + decoded, decErr := base64Decode(values[0]) + if decErr != nil { + return fmt.Errorf("error decoding base64 parameter '%s': %w", paramName, decErr) + } + v.SetBytes(decoded) + } else { + err = bindSplitPartsToDestinationArray(values, output) + } case reflect.Struct: // This case is really annoying, and error prone, but the // form style object binding doesn't tell us which arguments @@ -387,8 +488,14 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str var fieldsPresent bool fieldsPresent, err = bindParamsToExplodedObject(paramName, queryParams, output) // If no fields were set, and there is no error, we will not fall - // through to assign the destination. + // through to assign the destination. An absent required + // parameter is an error, matching the slice and primitive cases + // above; this also covers scalar struct types such as + // types.Date and time.Time. if !fieldsPresent { + if required { + return &RequiredParameterError{ParamName: paramName} + } return nil } default: @@ -396,7 +503,7 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str // unmarshal. if len(values) == 0 { if required { - return fmt.Errorf("query parameter '%s' is required", paramName) + return &RequiredParameterError{ParamName: paramName} } else { return nil } @@ -407,7 +514,7 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str if !found { if required { - return fmt.Errorf("query parameter '%s' is required", paramName) + return &RequiredParameterError{ParamName: paramName} } else { // If an optional parameter is not found, we do nothing, return nil @@ -429,7 +536,7 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str values, found := queryParams[paramName] if !found { if required { - return fmt.Errorf("query parameter '%s' is required", paramName) + return &RequiredParameterError{ParamName: paramName} } else { return nil } @@ -437,26 +544,285 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str if len(values) != 1 { return fmt.Errorf("parameter '%s' is not exploded, but is specified multiple times", paramName) } - parts = strings.Split(values[0], ",") + + // For primitive types, the raw value should be used as-is + // without splitting on commas. Per the OpenAPI specification, + // explode has no effect on primitive types — the serialization + // is the same regardless of the explode value. Comma splitting + // is only meaningful for array and object types. + // See: https://swagger.io/docs/specification/serialization/ + if k != reflect.Slice && k != reflect.Struct && k != reflect.Map { + err := BindStringToObject(values[0], output) + if err != nil { + return err + } + if extraIndirect { + dv.Set(reflect.ValueOf(output)) + } + return nil + } + + switch style { + case "spaceDelimited": + parts = strings.Split(values[0], " ") + case "pipeDelimited": + parts = strings.Split(values[0], "|") + default: + parts = strings.Split(values[0], ",") + } } var err error switch k { case reflect.Slice: - err = bindSplitPartsToDestinationArray(parts, output) - case reflect.Struct: - err = bindSplitPartsToDestinationStruct(paramName, parts, explode, output) - default: - if len(parts) == 0 { - if required { - return fmt.Errorf("query parameter '%s' is required", paramName) - } else { + if opts.Format == "byte" && isByteSlice(t) { + // For non-exploded form, the value was split on commas above. + // Rejoin to get the original base64 string. + raw := strings.Join(parts, ",") + decoded, decErr := base64Decode(raw) + if decErr != nil { + return fmt.Errorf("error decoding base64 parameter '%s': %w", paramName, decErr) + } + v.SetBytes(decoded) + } else { + err = bindSplitPartsToDestinationArray(parts, output) + } + case reflect.Struct, reflect.Map: + // Some struct types (e.g. types.Date, time.Time) are scalar values + // that should be bound from a single string, not decomposed as + // key-value objects. Detect these via the Binder and + // TextUnmarshaler interfaces. + switch v := output.(type) { + case Binder: + if len(parts) != 1 { + return fmt.Errorf("multiple values for single value parameter '%s'", paramName) + } + err = v.Bind(parts[0]) + case encoding.TextUnmarshaler: + if len(parts) != 1 { + return fmt.Errorf("multiple values for single value parameter '%s'", paramName) + } + err = v.UnmarshalText([]byte(parts[0])) + default: + err = bindSplitPartsToDestinationStruct(paramName, parts, explode, output) + } + } + if err != nil { + return err + } + if extraIndirect { + dv.Set(reflect.ValueOf(output)) + } + return nil + case "deepObject": + if !explode { + return errors.New("deepObjects must be exploded") + } + return unmarshalDeepObject(dest, paramName, queryParams, required) + default: + return fmt.Errorf("style '%s' on parameter '%s' is invalid", style, paramName) + + } +} + +// findRawQueryParam extracts the raw (still-percent-encoded) values for a given +// parameter name from a raw query string, without URL-decoding the values. +// The parameter key is decoded for comparison purposes, but the returned values +// remain in their original encoded form. +func findRawQueryParam(rawQuery, paramName string) (values []string, found bool) { + for rawQuery != "" { + var part string + if i := strings.IndexByte(rawQuery, '&'); i >= 0 { + part = rawQuery[:i] + rawQuery = rawQuery[i+1:] + } else { + part = rawQuery + rawQuery = "" + } + if part == "" { + continue + } + key := part + var val string + if i := strings.IndexByte(part, '='); i >= 0 { + key = part[:i] + val = part[i+1:] + } + decodedKey, err := url.QueryUnescape(key) + if err != nil { + // Skip malformed keys. + continue + } + if decodedKey == paramName { + values = append(values, val) + found = true + } + } + return values, found +} + +// BindRawQueryParameter works like BindQueryParameter but operates on the raw +// (undecoded) query string instead of pre-parsed url.Values. This correctly +// handles form/explode=false parameters whose values contain literal commas +// encoded as %2C — something that BindQueryParameter cannot do because +// url.Values has already decoded %2C to ',' before we can split on the +// delimiter comma. +func BindRawQueryParameter(style string, explode bool, required bool, paramName string, + rawQuery string, dest any) error { + + // dv = destination value. + dv := reflect.Indirect(reflect.ValueOf(dest)) + + // intermediate value form which is either dv or dv dereferenced. + v := dv + + // inner code will bind the string's value to this interface. + var output any + + // required params are never pointers, but it may happen that optional param + // is not pointer as well if user decides to annotate it with + // x-go-type-skip-optional-pointer + var extraIndirect = !required && v.Kind() == reflect.Pointer + if !extraIndirect { + output = dest + } else { + if v.IsNil() { + t := v.Type() + newValue := reflect.New(t.Elem()) + output = newValue.Interface() + } else { + output = v.Interface() + } + v = reflect.Indirect(reflect.ValueOf(output)) + } + + // This is the basic type of the destination object. + t := v.Type() + k := t.Kind() + + switch style { + case "form", "spaceDelimited", "pipeDelimited": + if explode { + // For the explode case, url.ParseQuery is fine — there are no + // delimiter commas to confuse with literal commas. + // + // Note: spaceDelimited and pipeDelimited with explode=true are + // serialized identically to form explode=true (each value is a + // separate key=value pair), so we share this code path. + queryParams, err := url.ParseQuery(rawQuery) + if err != nil { + return fmt.Errorf("error parsing query string: %w", err) + } + values, found := queryParams[paramName] + + switch k { + case reflect.Slice: + if !found { + if required { + return &RequiredParameterError{ParamName: paramName} + } + return nil + } + err = bindSplitPartsToDestinationArray(values, output) + case reflect.Struct: + var fieldsPresent bool + fieldsPresent, err = bindParamsToExplodedObject(paramName, queryParams, output) + // An absent required parameter is an error, matching the slice + // and primitive cases above; this also covers scalar struct + // types such as types.Date and time.Time. + if !fieldsPresent { + if required { + return &RequiredParameterError{ParamName: paramName} + } + return nil + } + default: + if len(values) == 0 { + if required { + return &RequiredParameterError{ParamName: paramName} + } + return nil + } + if len(values) != 1 { + return fmt.Errorf("multiple values for single value parameter '%s'", paramName) + } + if !found { + if required { + return &RequiredParameterError{ParamName: paramName} + } return nil } + err = BindStringToObject(values[0], output) } - if len(parts) != 1 { - return fmt.Errorf("multiple values for single value parameter '%s'", paramName) + if err != nil { + return err + } + if extraIndirect { + dv.Set(reflect.ValueOf(output)) + } + return nil + } + + // explode=false — use findRawQueryParam to get the still-encoded + // value, split on the style-specific delimiter, then URL-decode + // each resulting part individually. + rawValues, found := findRawQueryParam(rawQuery, paramName) + if !found { + if required { + return &RequiredParameterError{ParamName: paramName} + } + return nil + } + if len(rawValues) != 1 { + return fmt.Errorf("parameter '%s' is not exploded, but is specified multiple times", paramName) + } + + // For primitive types, decode the raw value as-is without splitting + // on commas. Per the OpenAPI specification, explode has no effect on + // primitive types. Comma splitting is only meaningful for array and + // object types. + if k != reflect.Slice && k != reflect.Struct && k != reflect.Map { + decoded, err := url.QueryUnescape(rawValues[0]) + if err != nil { + return fmt.Errorf("error decoding query parameter '%s' value %q: %w", paramName, rawValues[0], err) + } + err = BindStringToObject(decoded, output) + if err != nil { + return err + } + if extraIndirect { + dv.Set(reflect.ValueOf(output)) + } + return nil + } + + var rawParts []string + switch style { + case "spaceDelimited": + // Normalise all space representations to %20, then split. + normalized := strings.ReplaceAll(rawValues[0], "+", "%20") + normalized = strings.ReplaceAll(normalized, " ", "%20") + rawParts = strings.Split(normalized, "%20") + case "pipeDelimited": + rawParts = strings.Split(rawValues[0], "|") + default: + rawParts = strings.Split(rawValues[0], ",") + } + + parts := make([]string, len(rawParts)) + for i, rp := range rawParts { + decoded, err := url.QueryUnescape(rp) + if err != nil { + return fmt.Errorf("error decoding query parameter '%s' part %q: %w", paramName, rp, err) } - err = BindStringToObject(parts[0], output) + parts[i] = decoded + } + + var err error + switch k { + case reflect.Slice: + err = bindSplitPartsToDestinationArray(parts, output) + case reflect.Struct: + err = bindSplitPartsToDestinationStruct(paramName, parts, explode, output) } if err != nil { return err @@ -465,16 +831,18 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str dv.Set(reflect.ValueOf(output)) } return nil + case "deepObject": if !explode { return errors.New("deepObjects must be exploded") } + queryParams, err := url.ParseQuery(rawQuery) + if err != nil { + return fmt.Errorf("error parsing query string: %w", err) + } return UnmarshalDeepObject(dest, paramName, queryParams) - case "spaceDelimited", "pipeDelimited": - return fmt.Errorf("query arguments of style '%s' aren't yet supported", style) default: return fmt.Errorf("style '%s' on parameter '%s' is invalid", style, paramName) - } } diff --git a/vendor/github.com/oapi-codegen/runtime/bindstring.go b/vendor/github.com/oapi-codegen/runtime/bindstring.go index 764036d2a..ad4271e28 100644 --- a/vendor/github.com/oapi-codegen/runtime/bindstring.go +++ b/vendor/github.com/oapi-codegen/runtime/bindstring.go @@ -31,19 +31,40 @@ import ( // know the destination type each place that we use this, is to generate code // to read each specific type. func BindStringToObject(src string, dst interface{}) error { + return BindStringToObjectWithOptions(src, dst, BindStringToObjectOptions{}) +} + +// BindStringToObjectOptions defines optional arguments for BindStringToObjectWithOptions. +type BindStringToObjectOptions struct { + // Type is the OpenAPI type of the parameter (e.g. "string", "integer"). + Type string + // Format is the OpenAPI format of the parameter (e.g. "byte", "date-time"). + // When set to "byte" and the destination is []byte, the source string is + // base64-decoded rather than treated as a generic slice. + Format string +} + +// BindStringToObjectWithOptions takes a string, and attempts to assign it to the destination +// interface via whatever type conversion is necessary, with additional options. +func BindStringToObjectWithOptions(src string, dst interface{}, opts BindStringToObjectOptions) error { var err error + // Check if the destination implements Binder interface before any reflection + if binder, ok := dst.(Binder); ok { + return binder.Bind(src) + } + v := reflect.ValueOf(dst) t := reflect.TypeOf(dst) // We need to dereference pointers - if t.Kind() == reflect.Ptr { + if t.Kind() == reflect.Pointer { v = reflect.Indirect(v) t = v.Type() } // For some optional args - if t.Kind() == reflect.Ptr { + if t.Kind() == reflect.Pointer { if v.IsNil() { v.Set(reflect.New(t.Elem())) } @@ -59,6 +80,17 @@ func BindStringToObject(src string, dst interface{}) error { } switch t.Kind() { + case reflect.Slice: + if opts.Format == "byte" && isByteSlice(t) { + decoded, decErr := base64Decode(src) + if decErr != nil { + return fmt.Errorf("error binding string parameter: %w", decErr) + } + v.SetBytes(decoded) + return nil + } + // Non-binary slices fall through to the default error case. + fallthrough case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var val int64 val, err = strconv.ParseInt(src, 10, 64) @@ -107,11 +139,6 @@ func BindStringToObject(src string, dst interface{}) error { } fallthrough case reflect.Struct: - // if this is not of type Time or of type Date look to see if this is of type Binder. - if dstType, ok := dst.(Binder); ok { - return dstType.Bind(src) - } - if t.ConvertibleTo(reflect.TypeOf(time.Time{})) { // Don't fail on empty string. if src == "" { @@ -163,6 +190,21 @@ func BindStringToObject(src string, dst interface{}) error { // We fall through to the error case below if we haven't handled the // destination type above. fallthrough + case reflect.Map: + // A bool-keyed map (such as nullable.Nullable[T], which is + // map[bool]T) is treated as a nullable wrapper: bind src into a + // fresh value of the inner type and store it under map[true]. + if t.Kind() == reflect.Map && t.Key().Kind() == reflect.Bool { + elemPtr := reflect.New(t.Elem()) + if bindErr := BindStringToObjectWithOptions(src, elemPtr.Interface(), opts); bindErr != nil { + return bindErr + } + newMap := reflect.MakeMap(t) + newMap.SetMapIndex(reflect.ValueOf(true), elemPtr.Elem()) + v.Set(newMap) + return nil + } + fallthrough default: // We've got a bunch of types unimplemented, don't fail silently. err = fmt.Errorf("can not bind to destination of type: %s", t.Kind()) diff --git a/vendor/github.com/oapi-codegen/runtime/deepobject.go b/vendor/github.com/oapi-codegen/runtime/deepobject.go index 588465c03..ceda01f95 100644 --- a/vendor/github.com/oapi-codegen/runtime/deepobject.go +++ b/vendor/github.com/oapi-codegen/runtime/deepobject.go @@ -1,6 +1,8 @@ package runtime import ( + "bytes" + "encoding" "encoding/json" "errors" "fmt" @@ -52,10 +54,24 @@ func marshalDeepObject(in interface{}, path []string) ([]string, error) { default: // Now, for a concrete value, we will turn the path elements // into a deepObject style set of subscripts. [a, b, c] turns into - // [a][b][c] - prefix := "[" + strings.Join(path, "][") + "]" + // [a][b][c]. Path segments may originate from user-controlled map + // keys, so each segment is percent-encoded; the literal '[' and ']' + // remain as structural delimiters. + encoded := make([]string, len(path)) + for i, p := range path { + encoded[i] = DefaultQueryEncoder.EscapeQueryValue(p, false) + } + prefix := "[" + strings.Join(encoded, "][") + "]" + + var value string + if t == nil { + value = "null" + } else { + value = DefaultQueryEncoder.EscapeQueryValue(fmt.Sprintf("%v", t), false) + } + result = []string{ - prefix + fmt.Sprintf("=%v", t), + prefix + "=" + value, } } return result, nil @@ -71,8 +87,10 @@ func MarshalDeepObject(i interface{}, paramName string) (string, error) { if err != nil { return "", fmt.Errorf("failed to marshal input to JSON: %w", err) } + e := json.NewDecoder(bytes.NewReader(buf)) + e.UseNumber() var i2 interface{} - err = json.Unmarshal(buf, &i2) + err = e.Decode(&i2) if err != nil { return "", fmt.Errorf("failed to unmarshal JSON: %w", err) } @@ -81,9 +99,12 @@ func MarshalDeepObject(i interface{}, paramName string) (string, error) { return "", fmt.Errorf("error traversing JSON structure: %w", err) } - // Prefix the param name to each subscripted field. + // Prefix the param name to each subscripted field. The param name is + // percent-encoded to keep the wire output ASCII-clean even if the spec + // declares a non-identifier parameter name. + encodedParamName := DefaultQueryEncoder.EscapeQueryValue(paramName, false) for i := range fields { - fields[i] = paramName + fields[i] + fields[i] = encodedParamName + fields[i] } return strings.Join(fields, "&"), nil } @@ -123,7 +144,29 @@ func makeFieldOrValue(paths [][]string, values []string) fieldOrValue { return f } +// UnmarshalDeepObject decodes deepObject-style query parameters (e.g. +// `filter[name]=alice&filter[role]=admin`) into dst, using paramName as the +// outer prefix. +// +// Encoding: MarshalDeepObject percent-encodes values, map keys, and the +// param name itself, so any byte sequence — including non-ASCII text and URL +// reserved characters in values or in map keys — round-trips correctly. The +// '[' and ']' characters that appear at the top level of each fragment are +// always structural; literal brackets inside data are encoded as %5B/%5D on +// the wire. +// +// Known limitation: literal '[' or ']' inside map keys cannot be round- +// tripped. After url.ParseQuery decodes %5B/%5D back to '['/']', the parser +// cannot distinguish a structural bracket from a literal bracket that was +// part of a key (e.g. `p[a[b]]` is ambiguous between `{p: {a: {b: ...}}}` +// and `{p: {"a[b]": ...}}`). This matches the behavior of every other +// deepObject implementation (qs, Rails, PHP); the deepObject style itself +// does not define an escape mechanism for brackets inside keys. func UnmarshalDeepObject(dst interface{}, paramName string, params url.Values) error { + return unmarshalDeepObject(dst, paramName, params, false) +} + +func unmarshalDeepObject(dst interface{}, paramName string, params url.Values, required bool) error { // Params are all the query args, so we need those that look like // "paramName["... var fieldNames []string @@ -133,11 +176,24 @@ func UnmarshalDeepObject(dst interface{}, paramName string, params url.Values) e if strings.HasPrefix(pName, searchStr) { // trim the parameter name from the full name. pName = pName[len(paramName):] - fieldNames = append(fieldNames, pName) - if len(pValues) != 1 { - return fmt.Errorf("%s has multiple values", pName) + if len(pValues) == 1 { + fieldNames = append(fieldNames, pName) + fieldValues = append(fieldValues, pValues[0]) + } else { + // Non-indexed array format: expand repeated keys into indexed entries + for i, value := range pValues { + fieldNames = append(fieldNames, pName+"["+strconv.Itoa(i)+"]") + fieldValues = append(fieldValues, value) + } } - fieldValues = append(fieldValues, pValues[0]) + } + } + + if len(fieldNames) == 0 { + if required { + return fmt.Errorf("query parameter '%s' is required", paramName) + } else { + return nil } } @@ -248,6 +304,7 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error { dst = reflect.Indirect(aPtr) } dst.Set(reflect.ValueOf(date)) + return nil } if it.ConvertibleTo(reflect.TypeOf(time.Time{})) { var tm time.Time @@ -255,12 +312,10 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error { tm, err = time.Parse(time.RFC3339Nano, pathValues.value) if err != nil { // Fall back to parsing it as a date. - // TODO: why is this marked as an ineffassign? - tm, err = time.Parse(types.DateFormat, pathValues.value) //nolint:ineffassign,staticcheck + tm, err = time.Parse(types.DateFormat, pathValues.value) if err != nil { return fmt.Errorf("error parsing '%s' as RFC3339 or 2006-01-02 time: %w", pathValues.value, err) } - return fmt.Errorf("invalid date format: %w", err) } dst := iv if it != reflect.TypeOf(time.Time{}) { @@ -270,6 +325,13 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error { dst = reflect.Indirect(aPtr) } dst.Set(reflect.ValueOf(tm)) + return nil + } + // For other struct types that implement TextUnmarshaler (e.g. uuid.UUID), + // use that for binding. This comes after the legacy Date/time.Time checks + // above which have special fallback format handling. + if tu, ok := v.Interface().(encoding.TextUnmarshaler); ok { + return tu.UnmarshalText([]byte(pathValues.value)) } fieldMap, err := fieldIndicesByJSONTag(iv.Interface()) if err != nil { @@ -288,7 +350,7 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error { } } return nil - case reflect.Ptr: + case reflect.Pointer: // If we have a pointer after redirecting, it means we're dealing with // an optional field, such as *string, which was passed in as &foo. We // will allocate it if necessary, and call ourselves with a different @@ -298,6 +360,24 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error { err := assignPathValues(dstPtr, pathValues) iv.Set(dstVal) return err + case reflect.Interface: + // An empty interface carries no type information to bind against — + // this is the element type of the map[string]interface{} generated + // for `additionalProperties: true` (issue #138). Synthesize the + // generic value shapes encoding/json uses from the shape of the + // parsed fragment instead. + if it.NumMethod() != 0 { + // A non-empty interface can't be satisfied by a synthesized + // value; report it like any other unbindable destination. + return errors.New("unhandled type: " + it.String()) + } + val := interfaceFromFieldOrValue(pathValues) + if val == nil { + iv.Set(reflect.Zero(it)) + } else { + iv.Set(reflect.ValueOf(val)) + } + return nil case reflect.Bool: val, err := strconv.ParseBool(pathValues.value) if err != nil { @@ -334,27 +414,87 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error { } } +// interfaceFromFieldOrValue converts a parsed deepObject fragment into the +// generic value shapes encoding/json produces: map[string]interface{} for +// objects, []interface{} for arrays (consecutive integer subscripts, the +// shape MarshalDeepObject emits), and JSON scalars for leaf values. This +// round-trips a map[string]interface{} through MarshalDeepObject and back, +// up to the inherent ambiguities of the untyped wire format: a string that +// spells a JSON scalar ("true", "12.5") comes back as that scalar, and a +// map whose keys are exactly "0".."n-1" comes back as an array. +func interfaceFromFieldOrValue(fv fieldOrValue) interface{} { + if len(fv.fields) == 0 { + return jsonScalarFromString(fv.value) + } + if keys, ok := consecutiveIndices(fv.fields); ok { + arr := make([]interface{}, len(keys)) + for i, key := range keys { + arr[i] = interfaceFromFieldOrValue(fv.fields[key]) + } + return arr + } + m := make(map[string]interface{}, len(fv.fields)) + for key, value := range fv.fields { + m[key] = interfaceFromFieldOrValue(value) + } + return m +} + +// jsonScalarFromString maps raw query text to the value encoding/json would +// produce for the same literal: true/false, null, or a number (float64). +// Text that is not a JSON scalar stays a string — notably "00714", which the +// JSON number grammar rejects (leading zero), so zip-code-like values keep +// their exact form. +func jsonScalarFromString(s string) interface{} { + switch s { + case "true": + return true + case "false": + return false + case "null": + return nil + } + var n float64 + if err := json.Unmarshal([]byte(s), &n); err == nil { + return n + } + return s +} + +// consecutiveIndices reports whether the field keys are exactly the integer +// subscripts "0".."n-1" — the shape MarshalDeepObject emits for arrays — +// and returns the keys in index order. +func consecutiveIndices(fields map[string]fieldOrValue) ([]string, bool) { + keys := make([]string, len(fields)) + for k := range fields { + i, err := strconv.Atoi(k) + if err != nil || i < 0 || i >= len(fields) || keys[i] != "" { + return nil, false + } + keys[i] = k + } + return keys, true +} + func assignSlice(dst reflect.Value, pathValues fieldOrValue) error { - // Gather up the values nValues := len(pathValues.fields) - values := make([]string, nValues) - // We expect to have consecutive array indices in the map + + // Process each array element by index for i := 0; i < nValues; i++ { indexStr := strconv.Itoa(i) fv, found := pathValues.fields[indexStr] if !found { return errors.New("array deepObjects must have consecutive indices") } - values[i] = fv.value - } - // This could be cleaner, but we can call into assignPathValues to - // avoid recreating this logic. - for i := 0; i < nValues; i++ { + // Get the destination element dstElem := dst.Index(i).Addr() - err := assignPathValues(dstElem.Interface(), fieldOrValue{value: values[i]}) + + // assignPathValues handles both simple values (via fv.value) and + // nested objects (via fv.fields) automatically + err := assignPathValues(dstElem.Interface(), fv) if err != nil { - return fmt.Errorf("error binding array: %w", err) + return fmt.Errorf("error binding array element %d: %w", i, err) } } diff --git a/vendor/github.com/oapi-codegen/runtime/encoder.go b/vendor/github.com/oapi-codegen/runtime/encoder.go new file mode 100644 index 000000000..791935b19 --- /dev/null +++ b/vendor/github.com/oapi-codegen/runtime/encoder.go @@ -0,0 +1,77 @@ +// Copyright 2019 DeepMap, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package runtime + +import ( + "net/url" + "strings" +) + +// QueryEncoder escapes values destined for a URL query string. It governs +// query parameter values, names, and map keys; it does NOT affect path +// escaping or application/x-www-form-urlencoded request bodies, where encoding +// a space as '+' is the correct, media-type-defined behavior. +// +// Names and map keys are escaped as values with allowReserved=false, since +// allowReserved applies only to values per the OpenAPI spec. +// +// Generated clients route all query escaping through DefaultQueryEncoder. +// Provide a custom implementation, or use one of the built-ins +// (NetURLQueryEncoder, RFC3986QueryEncoder), to control how query strings are +// encoded on the wire. +type QueryEncoder interface { + // EscapeQueryValue escapes a query parameter value. When allowReserved is + // true, RFC 3986 reserved characters are left unencoded, per OpenAPI's + // allowReserved option (which applies to values only). + EscapeQueryValue(value string, allowReserved bool) string +} + +// NetURLQueryEncoder preserves Go's net/url behavior, encoding a space as '+'. +// This matches the application/x-www-form-urlencoded convention that browsers +// use for HTML forms. It is the default encoder, so existing generated clients +// are unaffected. Note that many, but not all, servers accept '+' as a space. +type NetURLQueryEncoder struct{} + +func (NetURLQueryEncoder) EscapeQueryValue(value string, allowReserved bool) string { + if allowReserved { + // The allowReserved encoding is already RFC 3986 compliant (space is + // encoded as %20), so it is identical for both built-in encoders. + return escapeQueryAllowReserved(value) + } + return url.QueryEscape(value) +} + +// RFC3986QueryEncoder encodes a space as %20 rather than '+', for strict RFC +// 3986 compliance. Some servers (for example, OData endpoints that expect +// filters such as ?filter=name%20eq%20'x') reject '+'-encoded spaces with 400 +// Bad Request. Set runtime.DefaultQueryEncoder to an RFC3986QueryEncoder to opt +// in. +// +// The +->%20 rewrite is lossless: url.QueryEscape encodes a literal '+' in the +// input as %2B, so any '+' remaining in its output can only be a space. +type RFC3986QueryEncoder struct{} + +func (RFC3986QueryEncoder) EscapeQueryValue(value string, allowReserved bool) string { + if allowReserved { + return escapeQueryAllowReserved(value) + } + return strings.ReplaceAll(url.QueryEscape(value), "+", "%20") +} + +// DefaultQueryEncoder is the QueryEncoder used by all generated clients to +// escape query parameters. It defaults to NetURLQueryEncoder for backwards +// compatibility. Set it once during program initialization; like +// http.DefaultClient, it is not safe to mutate concurrently with in-flight +// requests. +var DefaultQueryEncoder QueryEncoder = NetURLQueryEncoder{} diff --git a/vendor/github.com/oapi-codegen/runtime/paramformat.go b/vendor/github.com/oapi-codegen/runtime/paramformat.go new file mode 100644 index 000000000..045059303 --- /dev/null +++ b/vendor/github.com/oapi-codegen/runtime/paramformat.go @@ -0,0 +1,52 @@ +package runtime + +import ( + "encoding/base64" + "fmt" + "reflect" + "strings" +) + +// isByteSlice reports whether t is []byte (or equivalently []uint8). +func isByteSlice(t reflect.Type) bool { + return t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 +} + +// base64Decode decodes s as base64. +// +// Per OpenAPI 3.0, format: byte uses RFC 4648 Section 4 (standard alphabet, +// padded). We use padding presence to select the right decoder, rather than +// blindly cascading (which can produce corrupt output when RawStdEncoding +// silently accepts padded input and treats '=' as data). +// +// The logic: +// 1. If s contains '=' padding → standard padded decoder (Std or URL based on alphabet). +// 2. If s contains URL-safe characters ('_' or '-') → RawURLEncoding. +// 3. Otherwise → RawStdEncoding (unpadded, standard alphabet). +func base64Decode(s string) ([]byte, error) { + if s == "" { + return []byte{}, nil + } + + if strings.ContainsRune(s, '=') { + // Padded input. Pick alphabet based on whether URL-safe chars are present. + if strings.ContainsAny(s, "-_") { + return base64Decode1(base64.URLEncoding, s) + } + return base64Decode1(base64.StdEncoding, s) + } + + // Unpadded input. Pick alphabet based on whether URL-safe chars are present. + if strings.ContainsAny(s, "-_") { + return base64Decode1(base64.RawURLEncoding, s) + } + return base64Decode1(base64.RawStdEncoding, s) +} + +func base64Decode1(enc *base64.Encoding, s string) ([]byte, error) { + b, err := enc.DecodeString(s) + if err != nil { + return nil, fmt.Errorf("failed to base64-decode string %q: %w", s, err) + } + return b, nil +} diff --git a/vendor/github.com/oapi-codegen/runtime/styleparam.go b/vendor/github.com/oapi-codegen/runtime/styleparam.go index 8491c8503..08d87e4f5 100644 --- a/vendor/github.com/oapi-codegen/runtime/styleparam.go +++ b/vendor/github.com/oapi-codegen/runtime/styleparam.go @@ -16,6 +16,7 @@ package runtime import ( "bytes" "encoding" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -51,16 +52,41 @@ func StyleParam(style string, explode bool, paramName string, value interface{}) return StyleParamWithLocation(style, explode, paramName, ParamLocationUndefined, value) } -// Given an input value, such as a primitive type, array or object, turn it -// into a parameter based on style/explode definition, performing whatever -// escaping is necessary based on parameter location +// StyleParamWithLocation serializes a Go value into an OpenAPI-styled parameter +// string, performing escaping based on parameter location. func StyleParamWithLocation(style string, explode bool, paramName string, paramLocation ParamLocation, value interface{}) (string, error) { + return StyleParamWithOptions(style, explode, paramName, value, StyleParamOptions{ + ParamLocation: paramLocation, + }) +} + +// StyleParamOptions defines optional arguments for StyleParamWithOptions. +type StyleParamOptions struct { + // ParamLocation controls URL escaping behavior. + ParamLocation ParamLocation + // Type is the OpenAPI type of the parameter (e.g. "string", "integer"). + Type string + // Format is the OpenAPI format of the parameter (e.g. "byte", "date-time"). + // When set to "byte" and the value is []byte, it is base64-encoded as a + // single string rather than treated as a generic slice of uint8. + Format string + // Required indicates whether the parameter is required. + Required bool + // AllowReserved, when true, prevents percent-encoding of RFC 3986 + // reserved characters in query parameter values. Per the OpenAPI 3.x + // spec, this only applies to query parameters. + AllowReserved bool +} + +// StyleParamWithOptions serializes a Go value into an OpenAPI-styled parameter +// string with additional options. +func StyleParamWithOptions(style string, explode bool, paramName string, value interface{}, opts StyleParamOptions) (string, error) { t := reflect.TypeOf(value) v := reflect.ValueOf(value) // Things may be passed in by pointer, we need to dereference, so return // error on nil. - if t.Kind() == reflect.Ptr { + if t.Kind() == reflect.Pointer { if v.IsNil() { return "", fmt.Errorf("value is a nil pointer") } @@ -83,28 +109,32 @@ func StyleParamWithLocation(style string, explode bool, paramName string, paramL return "", fmt.Errorf("error marshaling '%s' as text: %w", value, err) } - return stylePrimitive(style, explode, paramName, paramLocation, string(b)) + return stylePrimitive(style, explode, paramName, opts.ParamLocation, opts.AllowReserved, string(b)) } } switch t.Kind() { case reflect.Slice: + if opts.Format == "byte" && isByteSlice(t) { + encoded := base64.StdEncoding.EncodeToString(v.Bytes()) + return stylePrimitive(style, explode, paramName, opts.ParamLocation, opts.AllowReserved, encoded) + } n := v.Len() sliceVal := make([]interface{}, n) for i := 0; i < n; i++ { sliceVal[i] = v.Index(i).Interface() } - return styleSlice(style, explode, paramName, paramLocation, sliceVal) + return styleSlice(style, explode, paramName, opts.ParamLocation, opts.AllowReserved, sliceVal) case reflect.Struct: - return styleStruct(style, explode, paramName, paramLocation, value) + return styleStruct(style, explode, paramName, opts.ParamLocation, opts.AllowReserved, value) case reflect.Map: - return styleMap(style, explode, paramName, paramLocation, value) + return styleMap(style, explode, paramName, opts.ParamLocation, opts.AllowReserved, value) default: - return stylePrimitive(style, explode, paramName, paramLocation, value) + return stylePrimitive(style, explode, paramName, opts.ParamLocation, opts.AllowReserved, value) } } -func styleSlice(style string, explode bool, paramName string, paramLocation ParamLocation, values []interface{}) (string, error) { +func styleSlice(style string, explode bool, paramName string, paramLocation ParamLocation, allowReserved bool, values []interface{}) (string, error) { if style == "deepObject" { if !explode { return "", errors.New("deepObjects must be exploded") @@ -115,6 +145,8 @@ func styleSlice(style string, explode bool, paramName string, paramLocation Para var prefix string var separator string + escapedName := escapeParameterName(paramName, paramLocation) + switch style { case "simple": separator = "," @@ -126,28 +158,28 @@ func styleSlice(style string, explode bool, paramName string, paramLocation Para separator = "," } case "matrix": - prefix = fmt.Sprintf(";%s=", paramName) + prefix = fmt.Sprintf(";%s=", escapedName) if explode { separator = prefix } else { separator = "," } case "form": - prefix = fmt.Sprintf("%s=", paramName) + prefix = fmt.Sprintf("%s=", escapedName) if explode { separator = "&" + prefix } else { separator = "," } case "spaceDelimited": - prefix = fmt.Sprintf("%s=", paramName) + prefix = fmt.Sprintf("%s=", escapedName) if explode { separator = "&" + prefix } else { separator = " " } case "pipeDelimited": - prefix = fmt.Sprintf("%s=", paramName) + prefix = fmt.Sprintf("%s=", escapedName) if explode { separator = "&" + prefix } else { @@ -163,7 +195,7 @@ func styleSlice(style string, explode bool, paramName string, paramLocation Para parts := make([]string, len(values)) for i, v := range values { part, err = primitiveToString(v) - part = escapeParameterString(part, paramLocation) + part = escapeParameterString(part, paramLocation, allowReserved) parts[i] = part if err != nil { return "", fmt.Errorf("error formatting '%s': %w", paramName, err) @@ -210,9 +242,9 @@ func marshalKnownTypes(value interface{}) (string, bool) { return "", false } -func styleStruct(style string, explode bool, paramName string, paramLocation ParamLocation, value interface{}) (string, error) { +func styleStruct(style string, explode bool, paramName string, paramLocation ParamLocation, allowReserved bool, value interface{}) (string, error) { if timeVal, ok := marshalKnownTypes(value); ok { - styledVal, err := stylePrimitive(style, explode, paramName, paramLocation, timeVal) + styledVal, err := stylePrimitive(style, explode, paramName, paramLocation, allowReserved, timeVal) if err != nil { return "", fmt.Errorf("failed to style time: %w", err) } @@ -240,7 +272,10 @@ func styleStruct(style string, explode bool, paramName string, paramLocation Par if err != nil { return "", fmt.Errorf("failed to unmarshal JSON: %w", err) } - s, err := StyleParamWithLocation(style, explode, paramName, paramLocation, i2) + s, err := StyleParamWithOptions(style, explode, paramName, i2, StyleParamOptions{ + ParamLocation: paramLocation, + AllowReserved: allowReserved, + }) if err != nil { return "", fmt.Errorf("error style JSON structure: %w", err) } @@ -269,7 +304,7 @@ func styleStruct(style string, explode bool, paramName string, paramLocation Par f := v.Field(i) // Unset optional fields will be nil pointers, skip over those. - if f.Type().Kind() == reflect.Ptr && f.IsNil() { + if f.Type().Kind() == reflect.Pointer && f.IsNil() { continue } str, err := primitiveToString(f.Interface()) @@ -279,10 +314,10 @@ func styleStruct(style string, explode bool, paramName string, paramLocation Par fieldDict[fieldName] = str } - return processFieldDict(style, explode, paramName, paramLocation, fieldDict) + return processFieldDict(style, explode, paramName, paramLocation, allowReserved, fieldDict) } -func styleMap(style string, explode bool, paramName string, paramLocation ParamLocation, value interface{}) (string, error) { +func styleMap(style string, explode bool, paramName string, paramLocation ParamLocation, allowReserved bool, value interface{}) (string, error) { if style == "deepObject" { if !explode { return "", errors.New("deepObjects must be exploded") @@ -299,10 +334,10 @@ func styleMap(style string, explode bool, paramName string, paramLocation ParamL } fieldDict[fieldName.String()] = str } - return processFieldDict(style, explode, paramName, paramLocation, fieldDict) + return processFieldDict(style, explode, paramName, paramLocation, allowReserved, fieldDict) } -func processFieldDict(style string, explode bool, paramName string, paramLocation ParamLocation, fieldDict map[string]string) (string, error) { +func processFieldDict(style string, explode bool, paramName string, paramLocation ParamLocation, allowReserved bool, fieldDict map[string]string) (string, error) { var parts []string // This works for everything except deepObject. We'll handle that one @@ -310,18 +345,20 @@ func processFieldDict(style string, explode bool, paramName string, paramLocatio if style != "deepObject" { if explode { for _, k := range sortedKeys(fieldDict) { - v := escapeParameterString(fieldDict[k], paramLocation) + v := escapeParameterString(fieldDict[k], paramLocation, allowReserved) parts = append(parts, k+"="+v) } } else { for _, k := range sortedKeys(fieldDict) { - v := escapeParameterString(fieldDict[k], paramLocation) + v := escapeParameterString(fieldDict[k], paramLocation, allowReserved) parts = append(parts, k) parts = append(parts, v) } } } + escapedName := escapeParameterName(paramName, paramLocation) + var prefix string var separator string @@ -341,13 +378,13 @@ func processFieldDict(style string, explode bool, paramName string, paramLocatio prefix = ";" } else { separator = "," - prefix = fmt.Sprintf(";%s=", paramName) + prefix = fmt.Sprintf(";%s=", escapedName) } case "form": if explode { separator = "&" } else { - prefix = fmt.Sprintf("%s=", paramName) + prefix = fmt.Sprintf("%s=", escapedName) separator = "," } case "deepObject": @@ -357,7 +394,7 @@ func processFieldDict(style string, explode bool, paramName string, paramLocatio } for _, k := range sortedKeys(fieldDict) { v := fieldDict[k] - part := fmt.Sprintf("%s[%s]=%s", paramName, k, v) + part := fmt.Sprintf("%s[%s]=%s", escapedName, k, v) parts = append(parts, part) } separator = "&" @@ -369,25 +406,27 @@ func processFieldDict(style string, explode bool, paramName string, paramLocatio return prefix + strings.Join(parts, separator), nil } -func stylePrimitive(style string, explode bool, paramName string, paramLocation ParamLocation, value interface{}) (string, error) { +func stylePrimitive(style string, explode bool, paramName string, paramLocation ParamLocation, allowReserved bool, value interface{}) (string, error) { strVal, err := primitiveToString(value) if err != nil { return "", err } + escapedName := escapeParameterName(paramName, paramLocation) + var prefix string switch style { case "simple": case "label": prefix = "." case "matrix": - prefix = fmt.Sprintf(";%s=", paramName) + prefix = fmt.Sprintf(";%s=", escapedName) case "form": - prefix = fmt.Sprintf("%s=", paramName) + prefix = fmt.Sprintf("%s=", escapedName) default: return "", fmt.Errorf("unsupported style '%s'", style) } - return prefix + escapeParameterString(strVal, paramLocation), nil + return prefix + escapeParameterString(strVal, paramLocation, allowReserved), nil } // Converts a primitive value to a string. We need to do this based on the @@ -452,6 +491,15 @@ func primitiveToString(value interface{}) (string, error) { default: v, ok := value.(fmt.Stringer) if !ok { + if kind == reflect.Struct || kind == reflect.Map { + // A nested object inside a styled parameter: OpenAPI + // style-based serialization is only defined for primitives, + // arrays and flat objects, so there is no wire format we + // could produce here. + return "", fmt.Errorf( + "cannot serialize nested object of type %s: style-based parameter serialization ('style'/'schema') is only defined for primitives, arrays, and flat objects; declare the parameter with 'content: application/json' instead, or map the schema to a Go type implementing fmt.Stringer", + reflect.TypeOf(value).String()) + } return "", fmt.Errorf("unsupported type %s", reflect.TypeOf(value).String()) } @@ -460,16 +508,57 @@ func primitiveToString(value interface{}) (string, error) { return output, nil } -// escapeParameterString escapes a parameter value bas on the location of that parameter. -// Query params and path params need different kinds of escaping, while header -// and cookie params seem not to need escaping. -func escapeParameterString(value string, paramLocation ParamLocation) string { +// escapeParameterName escapes a parameter name for use in query strings and +// paths. This ensures characters like [] in parameter names (e.g. user_ids[]) +// are properly percent-encoded per RFC 3986. +func escapeParameterName(name string, paramLocation ParamLocation) string { + // Parameter names should always be encoded regardless of allowReserved, + // which only applies to values per the OpenAPI spec. + return escapeParameterString(name, paramLocation, false) +} + +// escapeParameterString escapes a parameter value based on the location of +// that parameter. Query params and path params need different kinds of +// escaping, while header and cookie params seem not to need escaping. +// When allowReserved is true and the location is query, RFC 3986 reserved +// characters are left unencoded per the OpenAPI allowReserved specification. +func escapeParameterString(value string, paramLocation ParamLocation, allowReserved bool) string { switch paramLocation { case ParamLocationQuery: - return url.QueryEscape(value) + return DefaultQueryEncoder.EscapeQueryValue(value, allowReserved) case ParamLocationPath: return url.PathEscape(value) default: return value } } + +// escapeQueryAllowReserved percent-encodes a query parameter value while +// leaving RFC 3986 reserved characters (:/?#[]@!$&'()*+,;=) unencoded, as +// specified by OpenAPI's allowReserved parameter option. Only characters that +// are neither unreserved nor reserved are encoded (e.g., spaces, control +// characters, non-ASCII). +func escapeQueryAllowReserved(value string) string { + // RFC 3986 reserved characters that should NOT be encoded when + // allowReserved is true. + const reserved = `:/?#[]@!$&'()*+,;=` + + var buf strings.Builder + for _, b := range []byte(value) { + if isUnreserved(b) || strings.IndexByte(reserved, b) >= 0 { + buf.WriteByte(b) + } else { + fmt.Fprintf(&buf, "%%%02X", b) + } + } + return buf.String() +} + +// isUnreserved reports whether the byte is an RFC 3986 unreserved character: +// ALPHA / DIGIT / "-" / "." / "_" / "~" +func isUnreserved(c byte) bool { + return (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + c == '-' || c == '.' || c == '_' || c == '~' +} diff --git a/vendor/github.com/oapi-codegen/runtime/types/date.go b/vendor/github.com/oapi-codegen/runtime/types/date.go index 6ad852f63..155751ac4 100644 --- a/vendor/github.com/oapi-codegen/runtime/types/date.go +++ b/vendor/github.com/oapi-codegen/runtime/types/date.go @@ -12,7 +12,7 @@ type Date struct { } func (d Date) MarshalJSON() ([]byte, error) { - return json.Marshal(d.Time.Format(DateFormat)) + return json.Marshal(d.Format(DateFormat)) } func (d *Date) UnmarshalJSON(data []byte) error { @@ -30,7 +30,7 @@ func (d *Date) UnmarshalJSON(data []byte) error { } func (d Date) String() string { - return d.Time.Format(DateFormat) + return d.Format(DateFormat) } func (d *Date) UnmarshalText(data []byte) error { @@ -41,3 +41,18 @@ func (d *Date) UnmarshalText(data []byte) error { d.Time = parsed return nil } + +// Bind implements the runtime.Binder interface so that Date is treated as a +// scalar value when binding query parameters rather than being decomposed as +// a struct with key-value pairs. +func (d *Date) Bind(src string) error { + if src == "" { + return nil + } + parsed, err := time.Parse(DateFormat, src) + if err != nil { + return err + } + d.Time = parsed + return nil +} diff --git a/vendor/github.com/oapi-codegen/runtime/types/duration.go b/vendor/github.com/oapi-codegen/runtime/types/duration.go new file mode 100644 index 000000000..2ef80c881 --- /dev/null +++ b/vendor/github.com/oapi-codegen/runtime/types/duration.go @@ -0,0 +1,286 @@ +package types + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" + "time" +) + +// Duration represents an RFC 3339 duration (the ISO 8601 duration grammar +// referenced by the OpenAPI "duration" format), e.g. "P3Y6M4DT12H30M5S". +// See https://spec.openapis.org/registry/format/duration +// +// Calendar components (years, months, weeks, days) have no fixed length in +// wall-clock time, so the components are stored as parsed instead of being +// converted to a time.Duration; any spec-valid duration round-trips +// losslessly through ParseDuration and String. Use TimeDuration and +// FromTimeDuration to bridge to time.Duration where the conversion is +// well-defined. +// +// The RFC 3339 grammar makes weeks exclusive ("P2W" cannot be combined with +// other components); ParseDuration enforces that, but the struct cannot. A +// hand-built value mixing Weeks with other components serializes with the +// week between the months and days, which is outside the strict grammar. +type Duration struct { + Years int + Months int + Weeks int + Days int + + Hours int + Minutes int + // Seconds may carry a fraction (e.g. "PT0.5S"). Strictly, RFC 3339 + // allows only integer components; ISO 8601 permits a fraction on the + // smallest component, and real-world payloads use it, so fractional + // seconds are accepted when parsing and emitted when present. + Seconds float64 +} + +// designator ranks enforce component order and uniqueness within each part +// of the grammar: Y then M then D in the date part, H then M then S in the +// time part. +var ( + dateDesignators = map[byte]int{'Y': 0, 'M': 1, 'D': 2} + timeDesignators = map[byte]int{'H': 0, 'M': 1, 'S': 2} +) + +// ParseDuration parses an RFC 3339 duration string per the grammar in RFC +// 3339 appendix A: "P" followed by ordered date components ("1Y2M3D"), a +// time part introduced by "T" ("T4H5M6S"), or a standalone week component +// ("2W"). At least one component must be present. As an interoperability +// extension beyond the strict grammar, the seconds component may carry a +// fraction, with either a period or a comma ("PT0.5S", "PT0,5S"). +func ParseDuration(s string) (Duration, error) { + fail := func(msg string) (Duration, error) { + return Duration{}, fmt.Errorf("invalid RFC 3339 duration %q: %s", s, msg) + } + + if !strings.HasPrefix(s, "P") { + return fail("must start with 'P'") + } + rest := s[1:] + if rest == "" { + return fail("must contain at least one component") + } + + var d Duration + inTime := false + lastRank := -1 + components := 0 + + for len(rest) > 0 { + if rest[0] == 'T' { + if inTime { + return fail("repeated 'T'") + } + inTime = true + lastRank = -1 + rest = rest[1:] + if rest == "" { + return fail("'T' must be followed by a time component") + } + continue + } + + // Scan the integer part of the number. + intLen := 0 + for intLen < len(rest) && rest[intLen] >= '0' && rest[intLen] <= '9' { + intLen++ + } + if intLen == 0 { + return fail(fmt.Sprintf("expected a number before %q", rest)) + } + // Scan an optional fraction; only valid on the seconds component, + // which is checked once the designator is known. + numLen := intLen + hasFraction := false + if numLen < len(rest) && (rest[numLen] == '.' || rest[numLen] == ',') { + hasFraction = true + fracStart := numLen + 1 + numLen = fracStart + for numLen < len(rest) && rest[numLen] >= '0' && rest[numLen] <= '9' { + numLen++ + } + if numLen == fracStart { + return fail("fraction must contain digits") + } + } + if numLen >= len(rest) { + return fail("number must be followed by a designator") + } + number := rest[:numLen] + designator := rest[numLen] + rest = rest[numLen+1:] + components++ + + if designator == 'W' { + if inTime { + return fail("'W' is not valid in the time part") + } + if components != 1 || rest != "" { + return fail("a week component cannot be combined with other components") + } + if hasFraction { + return fail("only the seconds component may carry a fraction") + } + weeks, err := strconv.Atoi(number) + if err != nil { + return fail(fmt.Sprintf("invalid number %q: %v", number, err)) + } + d.Weeks = weeks + return d, nil + } + + designators := dateDesignators + if inTime { + designators = timeDesignators + } + rank, ok := designators[designator] + if !ok { + return fail(fmt.Sprintf("unexpected designator %q", string(designator))) + } + if rank <= lastRank { + return fail(fmt.Sprintf("component %q is out of order or repeated", string(designator))) + } + lastRank = rank + + if inTime && designator == 'S' { + seconds, err := strconv.ParseFloat(strings.Replace(number, ",", ".", 1), 64) + if err != nil { + return fail(fmt.Sprintf("invalid number %q: %v", number, err)) + } + d.Seconds = seconds + continue + } + if hasFraction { + return fail("only the seconds component may carry a fraction") + } + value, err := strconv.Atoi(number) + if err != nil { + return fail(fmt.Sprintf("invalid number %q: %v", number, err)) + } + switch { + case !inTime && designator == 'Y': + d.Years = value + case !inTime && designator == 'M': + d.Months = value + case !inTime && designator == 'D': + d.Days = value + case inTime && designator == 'H': + d.Hours = value + case inTime && designator == 'M': + d.Minutes = value + } + } + + return d, nil +} + +// String serializes the duration in RFC 3339 form, omitting zero components. +// The all-zero duration serializes as "PT0S". +func (d Duration) String() string { + var b strings.Builder + b.WriteByte('P') + writeComponent := func(value int, designator byte) { + if value != 0 { + b.WriteString(strconv.Itoa(value)) + b.WriteByte(designator) + } + } + writeComponent(d.Years, 'Y') + writeComponent(d.Months, 'M') + writeComponent(d.Weeks, 'W') + writeComponent(d.Days, 'D') + if d.Hours != 0 || d.Minutes != 0 || d.Seconds != 0 { + b.WriteByte('T') + writeComponent(d.Hours, 'H') + writeComponent(d.Minutes, 'M') + if d.Seconds != 0 { + b.WriteString(strconv.FormatFloat(d.Seconds, 'f', -1, 64)) + b.WriteByte('S') + } + } + if b.Len() == 1 { + return "PT0S" + } + return b.String() +} + +// TimeDuration converts to a time.Duration. Years and months have no fixed +// length, so their presence is an error; weeks and days are converted with +// the common fixed convention of 7-day weeks and 24-hour days, which ignores +// calendar effects such as DST transitions. +func (d Duration) TimeDuration() (time.Duration, error) { + if d.Years != 0 || d.Months != 0 { + return 0, errors.New("duration contains years or months, which have no fixed length") + } + return time.Duration(d.Weeks)*7*24*time.Hour + + time.Duration(d.Days)*24*time.Hour + + time.Duration(d.Hours)*time.Hour + + time.Duration(d.Minutes)*time.Minute + + time.Duration(d.Seconds*float64(time.Second)), nil +} + +// FromTimeDuration decomposes a time.Duration into hours, minutes and +// seconds. RFC 3339 durations cannot be negative, so a negative input is an +// error. +func FromTimeDuration(td time.Duration) (Duration, error) { + if td < 0 { + return Duration{}, errors.New("RFC 3339 durations cannot be negative") + } + var d Duration + d.Hours = int(td / time.Hour) + td -= time.Duration(d.Hours) * time.Hour + d.Minutes = int(td / time.Minute) + td -= time.Duration(d.Minutes) * time.Minute + d.Seconds = td.Seconds() + return d, nil +} + +func (d Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(d.String()) +} + +func (d *Duration) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + parsed, err := ParseDuration(s) + if err != nil { + return err + } + *d = parsed + return nil +} + +func (d Duration) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} + +func (d *Duration) UnmarshalText(data []byte) error { + parsed, err := ParseDuration(string(data)) + if err != nil { + return err + } + *d = parsed + return nil +} + +// Bind implements the runtime.Binder interface so that Duration is treated +// as a scalar value when binding parameters rather than being decomposed as +// a struct with key-value pairs. +func (d *Duration) Bind(src string) error { + if src == "" { + return nil + } + parsed, err := ParseDuration(src) + if err != nil { + return err + } + *d = parsed + return nil +} diff --git a/vendor/github.com/oapi-codegen/runtime/types/email.go b/vendor/github.com/oapi-codegen/runtime/types/email.go index e4a1cbdae..234e7ccf0 100644 --- a/vendor/github.com/oapi-codegen/runtime/types/email.go +++ b/vendor/github.com/oapi-codegen/runtime/types/email.go @@ -3,6 +3,7 @@ package types import ( "encoding/json" "errors" + "net/mail" ) // ErrValidationEmail is the sentinel error returned when an email fails validation @@ -14,11 +15,12 @@ var ErrValidationEmail = errors.New("email: failed to pass regex validation") type Email string func (e Email) MarshalJSON() ([]byte, error) { - if !emailRegex.MatchString(string(e)) { + m, err := mail.ParseAddress(string(e)) + if err != nil { return nil, ErrValidationEmail } - return json.Marshal(string(e)) + return json.Marshal(m.Address) } func (e *Email) UnmarshalJSON(data []byte) error { @@ -31,10 +33,11 @@ func (e *Email) UnmarshalJSON(data []byte) error { return err } - *e = Email(s) - if !emailRegex.MatchString(s) { + m, err := mail.ParseAddress(s) + if err != nil { return ErrValidationEmail } + *e = Email(m.Address) return nil } diff --git a/vendor/github.com/oapi-codegen/runtime/types/regexes.go b/vendor/github.com/oapi-codegen/runtime/types/regexes.go deleted file mode 100644 index 94f17df58..000000000 --- a/vendor/github.com/oapi-codegen/runtime/types/regexes.go +++ /dev/null @@ -1,11 +0,0 @@ -package types - -import "regexp" - -const ( - emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" -) - -var ( - emailRegex = regexp.MustCompile(emailRegexString) -) diff --git a/vendor/github.com/oasdiff/yaml/.golangci.toml b/vendor/github.com/oasdiff/yaml/.golangci.toml deleted file mode 100644 index 61b2b79a2..000000000 --- a/vendor/github.com/oasdiff/yaml/.golangci.toml +++ /dev/null @@ -1,16 +0,0 @@ -[run] -timeout = "120s" - -[output] -format = "colored-line-number" - -[linters] -enable = [ - "gocyclo", "unconvert", "goimports", "unused", "unused", - "vetshadow", "nakedret", "errcheck", "revive", "ineffassign", - "goconst", "vet", "unparam", "gofmt" -] - -[issues] -exclude-use-default = false - diff --git a/vendor/github.com/oasdiff/yaml/.golangci.yml b/vendor/github.com/oasdiff/yaml/.golangci.yml new file mode 100644 index 000000000..1324e85c3 --- /dev/null +++ b/vendor/github.com/oasdiff/yaml/.golangci.yml @@ -0,0 +1,26 @@ +version: "2" + +run: + timeout: 120s + +linters: + enable: + - errcheck + - goconst + - gocyclo + - govet + - ineffassign + - nakedret + - revive + - unconvert + - unparam + - unused + settings: + govet: + enable: + - shadow + +formatters: + enable: + - gofmt + - goimports diff --git a/vendor/github.com/oasdiff/yaml/README.md b/vendor/github.com/oasdiff/yaml/README.md index bdb01877b..dd6036e7a 100644 --- a/vendor/github.com/oasdiff/yaml/README.md +++ b/vendor/github.com/oasdiff/yaml/README.md @@ -1,14 +1,33 @@ # YAML marshaling and unmarshaling support for Go -[![Lint](https://github.com/invopop/yaml/actions/workflows/lint.yaml/badge.svg)](https://github.com/invopop/yaml/actions/workflows/lint.yaml) -[![Test Go](https://github.com/invopop/yaml/actions/workflows/test.yaml/badge.svg)](https://github.com/invopop/yaml/actions/workflows/test.yaml) -[![Go Report Card](https://goreportcard.com/badge/github.com/invopop/yaml)](https://goreportcard.com/report/github.com/invopop/yaml) -![Latest Tag](https://img.shields.io/github/v/tag/invopop/yaml) +[![Lint](https://github.com/oasdiff/yaml/actions/workflows/lint.yaml/badge.svg)](https://github.com/oasdiff/yaml/actions/workflows/lint.yaml) +[![Test Go](https://github.com/oasdiff/yaml/actions/workflows/test.yaml/badge.svg)](https://github.com/oasdiff/yaml/actions/workflows/test.yaml) +[![Go Report Card](https://goreportcard.com/badge/github.com/oasdiff/yaml)](https://goreportcard.com/report/github.com/oasdiff/yaml) +![Latest Tag](https://img.shields.io/github/v/tag/oasdiff/yaml) ## Fork -This fork is an improved version of the invopop/yaml package, designed to include line and column location information for YAML elements during unmarshalling. -To include location information use ```UnmarshalWithOrigin``` instead of ```Unmarshal```. -The heavy lifting is done by the underlying [oasdiff/yaml3](https://github.com/oasdiff/yaml3) package. +This fork is an improved version of the invopop/yaml package, designed to include line and column location information for YAML elements during unmarshalling. + +Origin tracking uses a two-pass approach: +1. `Unmarshal` decodes the YAML and extracts `__origin__` metadata injected by the underlying [oasdiff/yaml3](https://github.com/oasdiff/yaml3) decoder, returning an `*OriginTree` alongside the decoded struct. +2. The caller walks the `OriginTree` to apply file/line/column information to the decoded Go structs. + +`Unmarshal` is the single public unmarshal entry point. Pass `DecodeOpts{}` for a plain decode, or set fields to opt into origin tracking or YAML 1.1 timestamp-resolution suppression: + +```go +// Plain decode (no origin tracking, default YAML 1.1 behaviour): +_, err := yaml.Unmarshal(data, &v, yaml.DecodeOpts{}) + +// Decode with origin tracking: +tree, err := yaml.Unmarshal(data, &v, yaml.DecodeOpts{ + Origin: yaml.OriginOpt{Enabled: true, File: "myfile.yaml"}, +}) + +// Decode with timestamp resolution suppressed (date-shaped scalars stay strings): +_, err := yaml.Unmarshal(data, &v, yaml.DecodeOpts{DisableTimestamps: true}) +``` + +The returned `*OriginTree` mirrors the YAML document structure. Each node holds a compact `[]any` sequence with the file, key name, line, column, and locations of scalar fields and sequence items within that mapping. When `Origin.Enabled` is false, `nil` is returned for the tree with no overhead. ## Introduction @@ -44,13 +63,13 @@ GOOD: To install, run: ``` -$ go get github.com/invopop/yaml +$ go get github.com/oasdiff/yaml ``` And import using: ``` -import "github.com/invopop/yaml" +import "github.com/oasdiff/yaml" ``` Usage is very similar to the JSON library: @@ -61,7 +80,7 @@ package main import ( "fmt" - "github.com/invopop/yaml" + "github.com/oasdiff/yaml" ) type Person struct { @@ -85,7 +104,7 @@ func main() { // Unmarshal the YAML back into a Person struct. var p2 Person - err = yaml.Unmarshal(y, &p2) + _, err = yaml.Unmarshal(y, &p2, yaml.DecodeOpts{}) if err != nil { fmt.Printf("err: %v\n", err) return @@ -105,7 +124,7 @@ package main import ( "fmt" - "github.com/invopop/yaml" + "github.com/oasdiff/yaml" ) func main() { diff --git a/vendor/github.com/oasdiff/yaml/yaml.go b/vendor/github.com/oasdiff/yaml/yaml.go index c49678ee1..3ec5366b9 100644 --- a/vendor/github.com/oasdiff/yaml/yaml.go +++ b/vendor/github.com/oasdiff/yaml/yaml.go @@ -5,7 +5,7 @@ // uses json.Marshal and json.Unmarshal to convert to or from the struct. This // means that it effectively reuses the JSON struct tags as well as the custom // JSON methods MarshalJSON and UnmarshalJSON unlike go-yaml. -package yaml // import "github.com/invopop/yaml" +package yaml // import "github.com/oasdiff/yaml" import ( "bytes" @@ -16,7 +16,7 @@ import ( "reflect" "strconv" - "github.com/oasdiff/yaml3" + yaml "github.com/oasdiff/yaml3" ) // Marshal the object into JSON then converts JSON to YAML and returns the @@ -38,36 +38,172 @@ func Marshal(o interface{}) ([]byte, error) { // JSONOpt is a decoding option for decoding from JSON format. type JSONOpt func(*json.Decoder) *json.Decoder -// YAMLOpt is a decoding option for decoding from YAML format. -type YAMLOpt func(*yaml.Decoder) *yaml.Decoder +// OriginOpt controls origin-tracking behavior. When Enabled is false the +// OriginTree returned by Unmarshal is nil. +type OriginOpt struct { + // Enabled adds __origin__ metadata to maps during unmarshaling. + Enabled bool + // File is the source file name recorded in origin metadata. + File string +} -// Unmarshal converts YAML to JSON then uses JSON to unmarshal into an object, -// optionally configuring the behavior of the JSON unmarshal. -func Unmarshal(y []byte, o interface{}, opts ...JSONOpt) error { - return UnmarshalWithOrigin(y, o, false, opts...) +// DecodeOpts groups options that apply to the YAML decoder side, as opposed +// to JSONOpt which configures the JSON unmarshal step. +type DecodeOpts struct { + // Origin controls origin-tracking behavior. When Origin.Enabled is + // false the OriginTree returned by Unmarshal is nil. + Origin OriginOpt + // DisableTimestamps suppresses YAML 1.1 implicit-timestamp resolution. + // When true, untagged date-shaped scalars (e.g. "1344-08-22") resolve + // to strings instead of time.Time, which keeps map keys stable for + // real-world specs that use date-shaped strings as keys. Explicit + // "!!timestamp" tags in the source still resolve to time.Time. + DisableTimestamps bool } -// UnmarshalWithOrigin is like Unmarshal but if withOrigin is true, it will -// include the origin information in the output. -func UnmarshalWithOrigin(y []byte, o interface{}, withOrigin bool, opts ...JSONOpt) error { - dec := yaml.NewDecoder(bytes.NewReader(y)) - dec.Origin(withOrigin) - return unmarshal(dec, o, opts) +// OriginTree holds __origin__ data extracted from a YAML-decoded map tree. +// It mirrors the structure of the spec: Fields tracks map children by key, +// Items tracks slice children by index. +type OriginTree struct { + // File is the source file for all origins in this subtree. + // Set once at the root of each decode call; all nodes in the same + // decode share the same file. + File string + // Origin is the raw __origin__ value ([]any compact sequence) for this node. + // Format: [key_name, key_line, key_col, nf, f1_name, f1_delta, f1_col, ..., ns, ...] + Origin any + // Fields holds child trees keyed by map key name. + Fields map[string]*OriginTree + // Items holds child trees for slice elements (index-aligned). + Items []*OriginTree } -func unmarshal(dec *yaml.Decoder, o interface{}, opts []JSONOpt) error { +// Unmarshal converts YAML to JSON then uses JSON to unmarshal +// into o. It is the single public unmarshal entry point: pass DecodeOpts{} +// for the simple case, or set Origin / DisableTimestamps to opt into +// origin tracking or YAML 1.1 timestamp-resolution suppression. The +// variadic JSONOpt list configures the JSON unmarshal step. The returned +// OriginTree is nil when origin tracking is disabled. +func Unmarshal(y []byte, o interface{}, decode DecodeOpts, opts ...JSONOpt) (*OriginTree, error) { + dec := yaml.NewDecoder(bytes.NewReader(y)) + dec.Origin(decode.Origin.Enabled, decode.Origin.File) + if decode.DisableTimestamps { + dec.DisableTimestamps(true) + } + + // Decode YAML into a generic object. + var yamlObj interface{} + if err := dec.Decode(&yamlObj); err != nil { + if !errors.Is(err, io.EOF) { + return nil, fmt.Errorf("error converting YAML to JSON: %v", err) + } + } + + // Extract __origin__ before JSON conversion so the JSON stays small. + var tree *OriginTree + if decode.Origin.Enabled { + tree = extractOrigins(yamlObj, decode.Origin.File) + } + + // Convert to JSON (without __origin__) and unmarshal into the target struct. vo := reflect.ValueOf(o) - j, err := yamlToJSON(dec, &vo) + jsonObj, err := convertToJSONableObject(yamlObj, &vo) if err != nil { - return fmt.Errorf("error converting YAML to JSON: %v", err) + return nil, fmt.Errorf("error converting YAML to JSON: %v", err) } - - err = jsonUnmarshal(bytes.NewReader(j), o, opts...) + j, err := json.Marshal(jsonObj) if err != nil { - return fmt.Errorf("error unmarshaling JSON: %v", err) + return nil, fmt.Errorf("error converting YAML to JSON: %v", err) + } + if err := jsonUnmarshal(bytes.NewReader(j), o, opts...); err != nil { + return nil, fmt.Errorf("error unmarshaling JSON: %v", err) } - return nil + return tree, nil +} + +const originKey = "__origin__" + +// extractOrigins recursively extracts and removes __origin__ entries from a +// YAML-decoded map tree, returning the origin data as an OriginTree. +// file is the source file for all nodes in this decode call. +func extractOrigins(v any, file string) *OriginTree { + switch val := v.(type) { + case map[string]any: + return extractOriginsFromStringMap(val, file) + case map[interface{}]interface{}: + // yaml3 produces map[interface{}]interface{} when map keys are non-string + // (e.g. integer HTTP status codes like 200). __origin__ is always a string + // key, so we must handle this case to strip it from mixed-key maps. + tree := &OriginTree{File: file} + if orig, ok := val[originKey]; ok { + tree.Origin = orig + delete(val, originKey) + } + for k, child := range val { + if childTree := extractOrigins(child, file); childTree != nil { + if tree.Fields == nil { + tree.Fields = make(map[string]*OriginTree) + } + // Convert key to string: mirrors convertToJSONableObject behaviour. + // String keys pass through; int/int64/float64 keys are formatted. + var ks string + switch kt := k.(type) { + case string: + ks = kt + case int: + ks = strconv.Itoa(kt) + case int64: + ks = strconv.FormatInt(kt, 10) + case float64: + ks = strconv.FormatFloat(kt, 'g', -1, 64) + default: + ks = fmt.Sprintf("%v", k) + } + tree.Fields[ks] = childTree + } + } + if tree.Origin == nil && tree.Fields == nil { + return nil + } + return tree + case []any: + var items []*OriginTree + hasChild := false + for _, child := range val { + childTree := extractOrigins(child, file) + items = append(items, childTree) // may be nil; preserves index alignment + if childTree != nil { + hasChild = true + } + } + if !hasChild { + return nil + } + return &OriginTree{File: file, Items: items} + default: + return nil + } +} + +func extractOriginsFromStringMap(val map[string]any, file string) *OriginTree { + tree := &OriginTree{File: file} + if orig, ok := val[originKey]; ok { + tree.Origin = orig + delete(val, originKey) + } + for k, child := range val { + if childTree := extractOrigins(child, file); childTree != nil { + if tree.Fields == nil { + tree.Fields = make(map[string]*OriginTree) + } + tree.Fields[k] = childTree + } + } + if tree.Origin == nil && tree.Fields == nil { + return nil + } + return tree } // jsonUnmarshal unmarshals the JSON byte stream from the given reader into the @@ -142,6 +278,7 @@ func yamlToJSON(dec *yaml.Decoder, jsonTarget *reflect.Value) ([]byte, error) { return json.Marshal(jsonObj) } +// convertToJSONableObject converts a YAML object to a JSON-compatible object. func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (interface{}, error) { //nolint:gocyclo var err error diff --git a/vendor/github.com/oasdiff/yaml3/README.md b/vendor/github.com/oasdiff/yaml3/README.md index f08ebf4aa..3a218f229 100644 --- a/vendor/github.com/oasdiff/yaml3/README.md +++ b/vendor/github.com/oasdiff/yaml3/README.md @@ -2,7 +2,24 @@ Fork ---- -This fork is an improved version of the go-yaml/yaml package, designed to include line and column location information for YAML elements during unmarshalling. +This fork is an improved version of the go-yaml/yaml package, enhanced to inject `__origin__` metadata into YAML mapping nodes during decoding, recording the file, line, and column of each key. + +To enable origin tracking, call `Origin` on the decoder before decoding: + +```go +dec := yaml.NewDecoder(r) +dec.Origin(true, "myfile.yaml") + +var v interface{} +dec.Decode(&v) +// Each decoded map now contains a synthetic "__origin__" key whose value is +// a compact []interface{} sequence: +// [file, key_name, key_line, key_col, nf, f1_name, f1_delta, f1_col, ..., ns, s1_name, s1_count, (name, delta, col)×count, ...] +// where nf = number of scalar/sequence fields, ns = number of sequences with item locations, +// and deltas are line offsets from key_line. +``` + +Origin nodes are synthetic: their key node has `Line == 0` (since real YAML lines are 1-based), which allows callers to detect and strip them. The [oasdiff/yaml](https://github.com/oasdiff/yaml) package uses this to build an `OriginTree` before JSON conversion, keeping `__origin__` entirely out of the decoded struct. Introduction ------------ @@ -30,33 +47,19 @@ Specifically, as of v3 of the yaml package: - Does not support base-60 floats. These are gone from YAML 1.2, and were actually never supported by this package as it's clearly a poor choice. -and offers backwards -compatibility with YAML 1.1 in some cases. -1.2, including support for -anchors, tags, map merging, etc. Multi-document unmarshalling is not yet -implemented, and base-60 floats from YAML 1.1 are purposefully not -supported since they're a poor design and are gone in YAML 1.2. - Installation and usage ---------------------- -The import path for the package is *gopkg.in/yaml.v3*. +The import path for the package is *github.com/oasdiff/yaml3*. To install it, run: - go get gopkg.in/yaml.v3 + go get github.com/oasdiff/yaml3 API documentation ----------------- -If opened in a browser, the import path itself leads to the API documentation: - - - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) - -API stability -------------- - -The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). + - [https://pkg.go.dev/github.com/oasdiff/yaml3](https://pkg.go.dev/github.com/oasdiff/yaml3) License @@ -76,7 +79,7 @@ import ( "fmt" "log" - "oasdiff/yaml" + "github.com/oasdiff/yaml3" ) var data = ` diff --git a/vendor/github.com/oasdiff/yaml3/decode.go b/vendor/github.com/oasdiff/yaml3/decode.go index 0f2b9f997..f36d6bf65 100644 --- a/vendor/github.com/oasdiff/yaml3/decode.go +++ b/vendor/github.com/oasdiff/yaml3/decode.go @@ -30,12 +30,13 @@ import ( // Parser, produces a node tree out of a libyaml event stream. type parser struct { - parser yaml_parser_t - event yaml_event_t - doc *Node - anchors map[string]*Node - doneInit bool - textless bool + parser yaml_parser_t + event yaml_event_t + doc *Node + anchors map[string]*Node + doneInit bool + textless bool + disableTimestamps bool } func newParser(b []byte) *parser { @@ -175,7 +176,7 @@ func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { } else if defaultTag != "" { tag = defaultTag } else if kind == ScalarNode { - tag, _ = resolve("", value) + tag, _ = resolve("", value, p.disableTimestamps) } n := &Node{ Kind: kind, @@ -186,6 +187,13 @@ func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { if !p.textless { n.Line = p.event.start_mark.line + 1 n.Column = p.event.start_mark.column + 1 + // end_mark is the position just past this event. For scalars and aliases + // it already spans the whole node. For mappings and sequences this is the + // MAPPING-START/SEQUENCE-START event, so it only marks the start for now; + // mapping()/sequence() overwrite it from the matching END event so the + // span covers the whole block. + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 n.HeadComment = string(p.event.head_comment) n.LineComment = string(p.event.line_comment) n.FootComment = string(p.event.foot_comment) @@ -263,6 +271,26 @@ func (p *parser) sequence() *Node { } n.LineComment = string(p.event.line_comment) n.FootComment = string(p.event.foot_comment) + // End at the last item's end so the span reaches the end of the actual + // content, consistent with scalars/aliases. The SEQUENCE-END token sits at + // the start of the following line after a block dedent, which would + // overshoot the element. Empty sequences fall back to that token's mark. + if !p.textless { + if n.Style&FlowStyle != 0 { + // Flow collections close with an explicit `}`/`]`; the END token's + // mark is just past that delimiter, so the span covers the whole + // collection (and stays consistent with the empty-flow fallback, + // which has no last child and uses the same mark). + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 + } else if len(n.Content) > 0 { + last := n.Content[len(n.Content)-1] + n.EndLine, n.EndColumn = last.EndLine, last.EndColumn + } else { + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 + } + } p.expect(yaml_SEQUENCE_END_EVENT) return n } @@ -303,6 +331,28 @@ func (p *parser) mapping() *Node { n.Content[len(n.Content)-2].FootComment = n.FootComment n.FootComment = "" } + // End at the last entry's value end so the span reaches the end of the + // actual content, consistent with scalars/aliases. The MAPPING-END token + // sits at the start of the following line after a block dedent, which would + // overshoot the element. Empty mappings fall back to that token's mark. + // (Origin __origin__ nodes are appended later, during decode, so the last + // element here is a real value.) + if !p.textless { + if n.Style&FlowStyle != 0 { + // Flow collections close with an explicit `}`/`]`; the END token's + // mark is just past that delimiter, so the span covers the whole + // collection (and stays consistent with the empty-flow fallback, + // which has no last child and uses the same mark). + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 + } else if len(n.Content) > 0 { + last := n.Content[len(n.Content)-1] + n.EndLine, n.EndColumn = last.EndLine, last.EndColumn + } else { + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 + } + } p.expect(yaml_MAPPING_END_EVENT) return n } @@ -318,12 +368,14 @@ type decoder struct { stringMapType reflect.Type generalMapType reflect.Type - knownFields bool - origin bool - uniqueKeys bool - decodeCount int - aliasCount int - aliasDepth int + knownFields bool + origin bool + file string + uniqueKeys bool + decodeCount int + aliasCount int + aliasDepth int + disableTimestamps bool mergedFields map[interface{}]bool } @@ -525,6 +577,24 @@ func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { func (d *decoder) document(n *Node, out reflect.Value) (good bool) { if len(n.Content) == 1 { d.doc = n + if d.origin && d.aliasDepth == 0 { + root := n.Content[0] + if root.Kind == MappingNode && len(root.Content) >= 2 { + // Inject __origin__ into the root mapping of the document so that + // $ref-rooted YAML files (e.g. schemas/pet.yaml) expose origin + // metadata on their top-level schema, just like nested mappings do. + // Use the first key's position as the anchor for line-delta calculations. + firstKey := root.Content[0] + syntheticKey := &Node{ + Kind: ScalarNode, + Tag: "!!str", + Value: "", + Line: firstKey.Line, + Column: firstKey.Column, + } + addOriginInMap(syntheticKey, root, d.file) + } + } d.unmarshal(n.Content[0], out) return true } @@ -570,7 +640,7 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool { tag = strTag resolved = n.Value } else { - tag, resolved = resolve(n.Tag, n.Value) + tag, resolved = resolve(n.Tag, n.Value, d.disableTimestamps) if tag == binaryTag { data, err := base64.StdEncoding.DecodeString(resolved.(string)) if err != nil { @@ -751,8 +821,8 @@ func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { j := 0 for i := 0; i < l; i++ { e := reflect.New(et).Elem() - if d.origin { - addOriginInSeq(n.Content[i]) + if d.origin && d.aliasDepth == 0 { + addOriginInSeq(n.Content[i], d.file) } if ok := d.unmarshal(n.Content[i], e); ok { out.Index(j).Set(e) @@ -832,6 +902,24 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { mergeNode = n.Content[i+1] continue } + // __origin__ metadata entries are injected when the anchor is first processed. + // Decoding them through the normal path would count toward aliasCount and + // could spuriously trigger the excessive-aliasing check on large specs. + // Decode them with aliasDepth temporarily zeroed so they don't count + // toward aliasCount, but the origin data is still present in the output. + if d.aliasDepth > 0 && isOrigin(n.Content[i]) { + savedAliasDepth := d.aliasDepth + d.aliasDepth = 0 + k := reflect.New(kt).Elem() + if d.unmarshal(n.Content[i], k) { + e := reflect.New(et).Elem() + if d.unmarshal(n.Content[i+1], e) { + out.SetMapIndex(k, e) + } + } + d.aliasDepth = savedAliasDepth + continue + } k := reflect.New(kt).Elem() if d.unmarshal(n.Content[i], k) { if mergedFields != nil { @@ -850,8 +938,8 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } e := reflect.New(et).Elem() - if d.origin { - addOriginInMap(n.Content[i], n.Content[i+1]) + if d.origin && d.aliasDepth == 0 { + addOriginInMap(n.Content[i], n.Content[i+1], d.file) } if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) { out.SetMapIndex(k, e) diff --git a/vendor/github.com/oasdiff/yaml3/encode.go b/vendor/github.com/oasdiff/yaml3/encode.go index de9e72a3e..e4976f891 100644 --- a/vendor/github.com/oasdiff/yaml3/encode.go +++ b/vendor/github.com/oasdiff/yaml3/encode.go @@ -341,7 +341,7 @@ func (e *encoder) stringv(tag string, in reflect.Value) { // Check to see if it would resolve to a specific // tag when encoded unquoted. If it doesn't, // there's no need to quote it. - rtag, _ := resolve("", s) + rtag, _ := resolve("", s, false) canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) } // Note: it's possible for user code to emit invalid YAML @@ -446,7 +446,7 @@ func (e *encoder) node(node *Node, tail string) { if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { tag = "" } else { - rtag, _ := resolve("", node.Value) + rtag, _ := resolve("", node.Value, false) if rtag == stag { tag = "" } else if stag == strTag { diff --git a/vendor/github.com/oasdiff/yaml3/origin.go b/vendor/github.com/oasdiff/yaml3/origin.go index b8ffa2ca0..2edec4771 100644 --- a/vendor/github.com/oasdiff/yaml3/origin.go +++ b/vendor/github.com/oasdiff/yaml3/origin.go @@ -8,122 +8,133 @@ func isScalar(n *Node) bool { return n.Kind == ScalarNode } -func addOriginInSeq(n *Node) *Node { +func isSequence(n *Node) bool { + return n.Kind == SequenceNode +} + +func isMapping(n *Node) bool { + return n.Kind == MappingNode +} - if n.Kind != MappingNode { +func addOriginInSeq(n *Node, file string) *Node { + if !isMapping(n) || len(n.Content) == 0 { return n } - // in case of a sequence, we use the first element as the key - return addOrigin(n.Content[0], n) + return addOrigin(n.Content[0], n, file) } -func addOriginInMap(key, n *Node) *Node { - - if n.Kind != MappingNode { +func addOriginInMap(key, n *Node, file string) *Node { + if !isMapping(n) { return n } - - return addOrigin(key, n) + return addOrigin(key, n, file) } -func addOrigin(key, n *Node) *Node { +// addOrigin injects a compact __origin__ sequence into the mapping node n. +// +// Format: [file, key_name, key_line, key_col, nf, f1_name, f1_delta, f1_col, ..., ns, s1_name, s1_count, s1_l0_delta, s1_c0, ..., end_delta, end_col] +// +// - file: source file path +// - key_name: the YAML key whose value is this mapping +// - key_line, key_col: location of that key +// - nf: number of scalar+sequence fields recorded +// - per field: name (string), line delta from key_line (int), column (int) +// - ns: number of sequence fields that have item locations +// - per sequence: name (string), item count (int), then count × (line delta, col) +// - end_delta, end_col: end of the whole mapping block — line delta from +// key_line and absolute column of the position just past its last content. +// Appended last so a consumer that stops after the sequences section +// simply ignores it (backward compatible). +func addOrigin(key, n *Node, file string) *Node { if isOrigin(key) { return n } - n.Content = append(n.Content, getNamedMap(originTag, append(getKeyLocation(key), getNamedMap("fields", getFieldLocations(n))...))...) + seq := buildOriginSeq(key, n, file) + n.Content = append(n.Content, + &Node{Kind: ScalarNode, Tag: "!!str", Value: originTag}, // Line==0 → isOrigin + &Node{Kind: SequenceNode, Tag: "!!seq", Content: seq}, + ) return n } -func getFieldLocations(n *Node) []*Node { +func buildOriginSeq(key, n *Node, file string) []*Node { + // Header: file, key_name, key_line, key_col + nodes := []*Node{ + strNode(file), + strNode(key.Value), + intNode(key.Line), + intNode(key.Column), + } + + // Collect field and sequence data. + var fieldNodes []*Node // nf × (name, delta, col) + var seqNodes []*Node // ns × (name, count, (delta, col)…) + nf, ns := 0, 0 l := len(n.Content) - size := 0 for i := 0; i < l; i += 2 { - if isScalar(n.Content[i+1]) { - size += 2 + k := n.Content[i] + v := n.Content[i+1] + if isOrigin(k) { + continue + } + // Record the location of this field's key. + nf++ + fieldNodes = append(fieldNodes, + strNode(k.Value), + intNode(k.Line-key.Line), + intNode(k.Column), + ) + if isSequence(v) { + // Record locations of scalar items within the sequence. + // Format per item: value_str, line_delta, col + var itemNodes []*Node + for _, item := range v.Content { + if item.Kind == ScalarNode { + itemNodes = append(itemNodes, + strNode(item.Value), + intNode(item.Line-key.Line), + intNode(item.Column), + ) + } + } + if len(itemNodes) > 0 { + ns++ + seqNodes = append(seqNodes, strNode(k.Value), intNode(len(itemNodes)/3)) + seqNodes = append(seqNodes, itemNodes...) + } } } - nodes := make([]*Node, 0, size) - for i := 0; i < l; i += 2 { - if isScalar(n.Content[i+1]) { - nodes = append(nodes, getNodeLocation(n.Content[i])...) - } + nodes = append(nodes, intNode(nf)) + nodes = append(nodes, fieldNodes...) + nodes = append(nodes, intNode(ns)) + nodes = append(nodes, seqNodes...) + + // Block end: line delta from key_line and absolute end column of the whole + // mapping. Lets a consumer reconstruct the full block span + // [key_line, key_line+end_delta] -- e.g. an entire endpoint operation block. + endDelta, endCol := 0, 0 + if n.EndLine > 0 { + endDelta = n.EndLine - key.Line + endCol = n.EndColumn } + nodes = append(nodes, intNode(endDelta), intNode(endCol)) return nodes } -// isOrigin returns true if the key is an "origin" element -// the current implementation is not optimal, as it relies on the key's line number -// a better design would be to use a dedicated field in the Node struct +// isOrigin returns true if the key is a synthetic origin node. +// Synthetic nodes have Line==0 (real YAML lines are 1-based). func isOrigin(key *Node) bool { return key.Line == 0 } -func getNodeLocation(n *Node) []*Node { - return getNamedMap(n.Value, getLocationObject(n)) +func strNode(v string) *Node { + return &Node{Kind: ScalarNode, Tag: "!!str", Value: v} } -func getKeyLocation(n *Node) []*Node { - return getNamedMap("key", getLocationObject(n)) -} - -func getNamedMap(title string, content []*Node) []*Node { - if len(content) == 0 { - return nil - } - - return []*Node{ - { - Kind: ScalarNode, - Tag: "!!str", - Value: title, - }, - getMap(content), - } -} - -func getMap(content []*Node) *Node { - return &Node{ - Kind: MappingNode, - Tag: "!!map", - Content: content, - } -} - -func getLocationObject(key *Node) []*Node { - return []*Node{ - { - Kind: ScalarNode, - Tag: "!!str", - Value: "line", - }, - { - Kind: ScalarNode, - Tag: "!!int", - Value: fmt.Sprintf("%d", key.Line), - }, - { - Kind: ScalarNode, - Tag: "!!str", - Value: "column", - }, - { - Kind: ScalarNode, - Tag: "!!int", - Value: fmt.Sprintf("%d", key.Column), - }, - { - Kind: ScalarNode, - Tag: "!!str", - Value: "name", - }, - { - Kind: ScalarNode, - Tag: "!!string", - Value: key.Value, - }, - } +func intNode(v int) *Node { + return &Node{Kind: ScalarNode, Tag: "!!int", Value: fmt.Sprintf("%d", v)} } diff --git a/vendor/github.com/oasdiff/yaml3/resolve.go b/vendor/github.com/oasdiff/yaml3/resolve.go index 64ae88805..b35a08b80 100644 --- a/vendor/github.com/oasdiff/yaml3/resolve.go +++ b/vendor/github.com/oasdiff/yaml3/resolve.go @@ -123,7 +123,12 @@ func resolvableTag(tag string) bool { var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) -func resolve(tag string, in string) (rtag string, out interface{}) { +// resolve infers the resolved tag and Go value for an unmarshalled scalar. +// When disableTimestamps is true, the timestamp branch is skipped and +// date-shaped scalars (e.g. "1344-08-22") fall through to int / float / string +// resolution rather than being parsed into time.Time. Encode-side callers +// pass false to preserve the historical behaviour. +func resolve(tag string, in string, disableTimestamps bool) (rtag string, out interface{}) { tag = shortTag(tag) if !resolvableTag(tag) { return tag, in @@ -180,8 +185,11 @@ func resolve(tag string, in string) (rtag string, out interface{}) { case 'D', 'S': // Int, float, or timestamp. // Only try values as a timestamp if the value is unquoted or there's an explicit - // !!timestamp tag. - if tag == "" || tag == timestampTag { + // !!timestamp tag. disableTimestamps suppresses implicit tagging only: an explicit + // "!!timestamp" tag in the source still resolves to time.Time, since that is the + // caller's explicit intent. The flag's purpose is to keep date-shaped UNTAGGED + // scalars (e.g. map keys like "1344-08-22") as strings instead. + if (tag == "" && !disableTimestamps) || tag == timestampTag { t, ok := parseTimestamp(in) if ok { return timestampTag, t diff --git a/vendor/github.com/oasdiff/yaml3/scannerc.go b/vendor/github.com/oasdiff/yaml3/scannerc.go index ca0070108..7ee4f10e7 100644 --- a/vendor/github.com/oasdiff/yaml3/scannerc.go +++ b/vendor/github.com/oasdiff/yaml3/scannerc.go @@ -2309,6 +2309,12 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l return false } var leading_blank, trailing_blank bool + // content_end_mark tracks the position just past the last content character + // (captured after each content line is read, before its line break is + // consumed). The block scalar's end belongs here: end_mark otherwise advances + // to the start of the next line, which overshoots the span into the following + // node. Initialized to end_mark so an empty block scalar is unchanged. + content_end_mark := end_mark for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { // We are at the beginning of a non-empty line. @@ -2340,6 +2346,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l return false } } + content_end_mark = parser.mark // Consume the line break. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { @@ -2366,7 +2373,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, - end_mark: end_mark, + end_mark: content_end_mark, value: s, style: yaml_LITERAL_SCALAR_STYLE, } diff --git a/vendor/github.com/oasdiff/yaml3/yaml.go b/vendor/github.com/oasdiff/yaml3/yaml.go index a49a19b2d..f8095e301 100644 --- a/vendor/github.com/oasdiff/yaml3/yaml.go +++ b/vendor/github.com/oasdiff/yaml3/yaml.go @@ -89,9 +89,11 @@ func Unmarshal(in []byte, out interface{}) (err error) { // A Decoder reads and decodes YAML values from an input stream. type Decoder struct { - parser *parser - knownFields bool - origin bool + parser *parser + knownFields bool + origin bool + file string + disableTimestamps bool } // NewDecoder returns a new decoder that reads from r. @@ -112,8 +114,25 @@ func (dec *Decoder) KnownFields(enable bool) { // Origin enables the recording of the line and column of the // decoded values in the YAML content. -func (dec *Decoder) Origin(enable bool) { +func (dec *Decoder) Origin(enable bool, file string) { dec.origin = enable + dec.file = file +} + +// DisableTimestamps controls whether YAML 1.1 implicit-timestamp resolution +// is applied during decoding. +// +// When false (the default), unquoted scalars matching the YAML timestamp +// grammar (e.g. "1344-08-22") are resolved to time.Time values, including +// when used as map keys. Setting disable to true skips that branch so such +// scalars resolve to strings instead. +// +// Explicit "!!timestamp" tags in the source continue to resolve to time.Time, +// because the explicit tag is the caller's intent. Use this flag for input +// where date-shaped UNTAGGED scalars are intended as strings, for example +// OpenAPI specs that use "1344-08-22" as a map key. +func (dec *Decoder) DisableTimestamps(disable bool) { + dec.disableTimestamps = disable } // Decode reads the next YAML-encoded value from its input @@ -125,6 +144,9 @@ func (dec *Decoder) Decode(v interface{}) (err error) { d := newDecoder() d.knownFields = dec.knownFields d.origin = dec.origin + d.file = dec.file + d.disableTimestamps = dec.disableTimestamps + dec.parser.disableTimestamps = dec.disableTimestamps defer handleErr(&err) node := dec.parser.parse() if node == nil { @@ -417,6 +439,14 @@ type Node struct { // These fields are not respected when encoding the node. Line int Column int + + // EndLine and EndColumn hold the position just past the end of the node in + // the decoded YAML text. For a mapping or sequence this spans the whole + // block (so a caller can extract the entire collection), not just its first + // line. Like Line and Column, these are 1-based and are not respected when + // encoding the node. + EndLine int + EndColumn int } // IsZero returns whether the node has all of its fields unset. @@ -450,7 +480,7 @@ func (n *Node) ShortTag() string { return n.Alias.ShortTag() } case ScalarNode: - tag, _ := resolve("", n.Value) + tag, _ := resolve("", n.Value, false) return tag case 0: // Special case to make the zero value convenient. diff --git a/vendor/github.com/perimeterx/marshmallow/.gitignore b/vendor/github.com/perimeterx/marshmallow/.gitignore deleted file mode 100644 index cf53c0a1b..000000000 --- a/vendor/github.com/perimeterx/marshmallow/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.idea - -coverage.out -profile.out diff --git a/vendor/github.com/perimeterx/marshmallow/CHANGELOG.md b/vendor/github.com/perimeterx/marshmallow/CHANGELOG.md deleted file mode 100644 index 92937d057..000000000 --- a/vendor/github.com/perimeterx/marshmallow/CHANGELOG.md +++ /dev/null @@ -1,49 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [[1.1.5](https://github.com/PerimeterX/marshmallow/compare/v1.1.4...v1.1.5)] - 2023-07-03 - -### Added - -- Support for reporting errors from `HandleJSONData` - [info](https://github.com/PerimeterX/marshmallow/issues/27). - -## [[1.1.4](https://github.com/PerimeterX/marshmallow/compare/v1.1.3...v1.1.4)] - 2022-11-10 - -### Fixed - -- Fixed problem with nested object implementing JSONDataHandler with skipPopulateStruct - [info](https://github.com/PerimeterX/marshmallow/issues/18). -- Fixed problem with nested object implementing JSONDataHandler with skipPopulateStruct in ModeFailOverToOriginalValue - [info](https://github.com/PerimeterX/marshmallow/issues/19). - -## [[1.1.3](https://github.com/PerimeterX/marshmallow/compare/v1.1.2...v1.1.3)] - 2022-08-31 - -### Added - -- Support for excluding known fields from the result map - [info](https://github.com/PerimeterX/marshmallow/issues/16). - -## [[1.1.2](https://github.com/PerimeterX/marshmallow/compare/v1.1.1...v1.1.2)] - 2022-08-23 - -### Added - -- Support capturing nested unknown fields - [info](https://github.com/PerimeterX/marshmallow/issues/15). - -## [[1.1.1](https://github.com/PerimeterX/marshmallow/compare/v1.1.0...v1.1.1)] - 2022-08-21 - -### Fixed - -- Fix parsing bug for unknown nested fields - [info](https://github.com/PerimeterX/marshmallow/issues/12). - -## [[1.1.0](https://github.com/PerimeterX/marshmallow/compare/v0.0.1...v1.1.0)] - 2022-07-10 - -### Fixed - -- Fixed an issue with embedded fields - [info](https://github.com/PerimeterX/marshmallow/issues/9). - -## [[0.0.1](https://github.com/PerimeterX/marshmallow/tree/v0.0.1)] - 2022-04-21 - -### Added - -- All functionality from our internal repository, after it has been stabilized on production for several months - [info](https://www.perimeterx.com/tech-blog/2022/boosting-up-json-performance-of-unstructured-structs-in-go/). diff --git a/vendor/github.com/perimeterx/marshmallow/CODE_OF_CONDUCT.md b/vendor/github.com/perimeterx/marshmallow/CODE_OF_CONDUCT.md deleted file mode 100644 index 0f6c45e79..000000000 --- a/vendor/github.com/perimeterx/marshmallow/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,133 +0,0 @@ - -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or advances of - any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -[opensource-conduct@humansecurity.com](mailto:opensource-conduct@humansecurity.com). -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations diff --git a/vendor/github.com/perimeterx/marshmallow/CONTRIBUTING.md b/vendor/github.com/perimeterx/marshmallow/CONTRIBUTING.md deleted file mode 100644 index a265c9ab9..000000000 --- a/vendor/github.com/perimeterx/marshmallow/CONTRIBUTING.md +++ /dev/null @@ -1,47 +0,0 @@ -# How To Contribute - -We'd love to accept your patches and contributions to this project. There are just a few guidelines you need to follow which are described in detail below. - -## 1. Fork this repo - -You should create a fork of this project in your account and work from there. You can create a fork by clicking the fork button in GitHub. - -## 2. One feature, one branch - -Work for each new feature/issue should occur in its own branch. To create a new branch from the command line: -```shell -git checkout -b my-new-feature -``` -where "my-new-feature" describes what you're working on. - -## 3. Add unit tests -If your contribution modifies existing or adds new code please add corresponding unit tests for this. - -## 4. Ensure that the build passes - -Run -```shell -go test -v -``` -and check that there are no errors. - -## 5. Add documentation for new or updated functionality - -Please review the [README.md](README.md) file in this project to see if they are impacted by your change and update them accordingly. - -## 6. Add to CHANGELOG.md - -Any notable changes should be recorded in the CHANGELOG.md following the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) conventions. - -## 7. Submit a pull request and describe the change - -Push your changes to your branch and open a pull request against the parent repo on GitHub. The project administrators will review your pull request and respond with feedback. - -# How your contribution gets merged - -Upon pull request submission, your code will be reviewed by the maintainers. They will confirm at least the following: - -- Tests run successfully (unit, coverage, style). -- Contribution policy has been followed. - -A (human) reviewer will need to sign off on your pull request before it can be merged. diff --git a/vendor/github.com/perimeterx/marshmallow/README.md b/vendor/github.com/perimeterx/marshmallow/README.md deleted file mode 100644 index bfa903637..000000000 --- a/vendor/github.com/perimeterx/marshmallow/README.md +++ /dev/null @@ -1,205 +0,0 @@ -# Marshmallow - -![Marshmallow Campfire](https://raw.githubusercontent.com/PerimeterX/marshmallow/assets/campfire.png) - -[![CodeQL Status](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/codeql.yml?branch=main&logo=github&label=CodeQL)](https://github.com/PerimeterX/marshmallow/actions/workflows/codeql.yml?query=branch%3Amain++) -[![Run Tests](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/go.yml?branch=main&logo=github&label=Run%20Tests)](https://github.com/PerimeterX/marshmallow/actions/workflows/go.yml?query=branch%3Amain) -[![Dependency Review](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/dependency-review.yml?logo=github&label=Dependency%20Review)](https://github.com/PerimeterX/marshmallow/actions/workflows/dependency-review.yml?query=branch%3Amain) -[![Go Report Card](https://goreportcard.com/badge/github.com/perimeterx/marshmallow)](https://goreportcard.com/report/github.com/perimeterx/marshmallow) -![Manual Code Coverage](https://img.shields.io/badge/coverage-92.6%25-green) -[![Go Reference](https://pkg.go.dev/badge/github.com/perimeterx/marshmallow.svg)](https://pkg.go.dev/github.com/perimeterx/marshmallow) -[![Licence](https://img.shields.io/github/license/perimeterx/marshmallow)](LICENSE) -[![Latest Release](https://img.shields.io/github/v/release/perimeterx/marshmallow)](https://github.com/PerimeterX/marshmallow/releases) -![Top Languages](https://img.shields.io/github/languages/top/perimeterx/marshmallow) -[![Issues](https://img.shields.io/github/issues-closed/perimeterx/marshmallow?color=%238250df&logo=github)](https://github.com/PerimeterX/marshmallow/issues) -[![Pull Requests](https://img.shields.io/github/issues-pr-closed-raw/perimeterx/marshmallow?color=%238250df&label=merged%20pull%20requests&logo=github)](https://github.com/PerimeterX/marshmallow/pulls) -[![Commits](https://img.shields.io/github/last-commit/perimeterx/marshmallow)](https://github.com/PerimeterX/marshmallow/commits/main) -[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) - -marshmallow-gopher - -Marshmallow package provides a simple API to perform flexible and performant JSON unmarshalling in Go. - -Marshmallow specializes in dealing with **unstructured struct** - when some fields are known and some aren't, -with zero performance overhead nor extra coding needed. -While unmarshalling, marshmallow allows fully retaining the original data and access -it via a typed struct and a dynamic map. - -## Contents - -- [Install](#install) -- [Usage](#usage) -- [Performance Benchmark And Alternatives](#performance-benchmark-and-alternatives) -- [When Should I Use Marshmallow](#when-should-i-use-marshmallow) -- [API](#api) - -## Install - -```sh -go get -u github.com/perimeterx/marshmallow -``` - -## Usage - -```go -package main - -import ( - "fmt" - "github.com/perimeterx/marshmallow" -) - -func main() { - v := struct { - Foo string `json:"foo"` - Boo []int `json:"boo"` - }{} - result, err := marshmallow.Unmarshal([]byte(`{"foo":"bar","boo":[1,2,3],"goo":12.6}`), &v) - fmt.Printf("v=%+v, result=%+v, err=%v", v, result, err) - // Output: v={Foo:bar Boo:[1 2 3]}, result=map[boo:[1 2 3] foo:bar goo:12.6], err= -} -``` - -**Examples can be found [here](example_test.go)** - -## Performance Benchmark And Alternatives - -Marshmallow performs best when dealing with mixed data - when some fields are known and some are unknown. -More info [below](#when-should-i-use-marshmallow). -Other solutions are available for this kind of use case, each solution is explained and documented in the link below. -The full benchmark test can be found -[here](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go). - -|Benchmark|Iterations|Time/Iteration|Bytes Allocated|Allocations| -|--|--|--|--|--| -|[unmarshall twice](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L40)|228693|5164 ns/op|1640 B/op|51 allocs/op| -|[raw map](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L66)|232236|5116 ns/op|2296 B/op|53 allocs/op| -|[go codec](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L121)|388442|3077 ns/op|2512 B/op|37 allocs/op| -|[marshmallow](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L16)|626168|1853 ns/op|608 B/op|18 allocs/op| -|[marshmallow without populating struct](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L162)|678616|1751 ns/op|608 B/op|18 allocs/op| - -![marshmallow performance comparison](https://raw.githubusercontent.com/PerimeterX/marshmallow/e45088ca20d4ea5be4143d418d12da63a68d6dfd/performance-chart.svg) - -**Marshmallow provides the best performance (up to X3 faster) while not requiring any extra coding.** -In fact, marshmallow performs as fast as normal `json.Unmarshal` call, however, such a call causes loss of data for all -the fields that did not match the given struct. With marshmallow you never lose any data. - -|Benchmark|Iterations|Time/Iteration|Bytes Allocated|Allocations| -|--|--|--|--|--| -|[marshmallow](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L16)|626168|1853 ns/op|608 B/op|18 allocs/op| -|[native library](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L143)|652106|1845 ns/op|304 B/op|11 allocs/op| -|[marshmallow without populating struct](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L162)|678616|1751 ns/op|608 B/op|18 allocs/op| - -## When Should I Use Marshmallow - -Marshmallow is best suited for use cases where you are interested in all the input data, but you have predetermined -information only about a subset of it. For instance, if you plan to reference two specific fields from the data, then -iterate all the data and apply some generic logic. How does it look with the native library: - -```go -func isAllowedToDrive(data []byte) (bool, error) { - result := make(map[string]interface{}) - err := json.Unmarshal(data, &result) - if err != nil { - return false, err - } - - age, ok := result["age"] - if !ok { - return false, nil - } - a, ok := age.(float64) - if !ok { - return false, nil - } - if a < 17 { - return false, nil - } - - hasDriversLicense, ok := result["has_drivers_license"] - if !ok { - return false, nil - } - h, ok := hasDriversLicense.(bool) - if !ok { - return false, nil - } - if !h { - return false, nil - } - - for key := range result { - if strings.Contains(key, "prior_conviction") { - return false, nil - } - } - - return true, nil -} -``` - -And with marshmallow: - -```go -func isAllowedToDrive(data []byte) (bool, error) { - v := struct { - Age int `json:"age"` - HasDriversLicense bool `json:"has_drivers_license"` - }{} - result, err := marshmallow.Unmarshal(data, &v) - if err != nil { - return false, err - } - - if v.Age < 17 || !v.HasDriversLicense { - return false, nil - } - - for key := range result { - if strings.Contains(key, "prior_conviction") { - return false, nil - } - } - - return true, nil -} -``` - -## API - -Marshmallow exposes two main API functions - -[Unmarshal](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/unmarshal.go#L27) -and -[UnmarshalFromJSONMap](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/unmarshal_from_json_map.go#L37). -While unmarshalling, marshmallow supports the following optional options: - -* Setting the mode for handling invalid data using the [WithMode](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/options.go#L30) function. -* Excluding known fields from the result map using the [WithExcludeKnownFieldsFromMap](https://github.com/PerimeterX/marshmallow/blob/457669ae9973895584f2636eabfc104140d3b700/options.go#L50) function. -* Skipping struct population to boost performance using the [WithSkipPopulateStruct](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/options.go#L41) function. - -In order to capture unknown nested fields, structs must implement [JSONDataErrorHandler](https://github.com/PerimeterX/marshmallow/blob/195c994aa6e3e0852601ad9cf65bcddef0dd7479/options.go#L76). -More info [here](https://github.com/PerimeterX/marshmallow/issues/15). - -Marshmallow also supports caching of refection information using -[EnableCache](https://github.com/PerimeterX/marshmallow/blob/d3500aa5b0f330942b178b155da933c035dd3906/cache.go#L40) -and -[EnableCustomCache](https://github.com/PerimeterX/marshmallow/blob/d3500aa5b0f330942b178b155da933c035dd3906/cache.go#L35). - -## Contact and Contribute - -Reporting issues and requesting features may be done in our [GitHub issues page](https://github.com/PerimeterX/marshmallow/issues). -Discussions may be conducted in our [GitHub discussions page](https://github.com/PerimeterX/marshmallow/discussions). -For any further questions or comments you can reach us out at [open-source@humansecurity.com](mailto:open-source@humansecurity.com). - -Any type of contribution is warmly welcome and appreciated ❤️ -Please read our [contribution](CONTRIBUTING.md) guide for more info. - -If you're looking for something to get started with, tou can always follow our [issues page](https://github.com/PerimeterX/marshmallow/issues) and look for -[good first issue](https://github.com/PerimeterX/marshmallow/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) and -[help wanted](https://github.com/PerimeterX/marshmallow/issues?q=is%3Aissue+label%3A%22help+wanted%22+is%3Aopen) labels. - -## Marshmallow Logo - -Marshmallow logo and assets by [Adva Rom](https://www.linkedin.com/in/adva-rom-7a6738127/) are licensed under a Creative Commons Attribution 4.0 International License.
- -![Marshmallow Logo](https://raw.githubusercontent.com/PerimeterX/marshmallow/assets/marshmallow.png) diff --git a/vendor/github.com/perimeterx/marshmallow/cache.go b/vendor/github.com/perimeterx/marshmallow/cache.go deleted file mode 100644 index a67cea6d8..000000000 --- a/vendor/github.com/perimeterx/marshmallow/cache.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2022 PerimeterX. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package marshmallow - -import ( - "reflect" - "sync" -) - -// Cache allows unmarshalling to use a cached version of refection information about types. -// Cache interface follows the implementation of sync.Map, but you may wrap any cache implementation -// to match it. This allows you to control max cache size, eviction policies and any other caching aspect. -type Cache interface { - // Load returns the value stored in the map for a key, or nil if no value is present. - // The ok result indicates whether value was found in the map. - Load(key interface{}) (interface{}, bool) - // Store sets the value for a key. - Store(key, value interface{}) -} - -// EnableCustomCache enables unmarshalling cache. It allows reuse of refection information about types needed -// to perform the unmarshalling. A use of such cache can boost up unmarshalling by x1.4. -// Check out benchmark_test.go for an example. -// -// EnableCustomCache is not thread safe! Do not use it while performing unmarshalling, or it will -// cause an unsafe race condition. Typically, EnableCustomCache should be called once when the process boots. -// -// Caching is disabled by default. The use of this function allows enabling it and controlling the -// behavior of the cache. Typically, the use of sync.Map should be good enough. The caching mechanism -// stores a single map per struct type. If you plan to unmarshal a huge amount of distinct -// struct it may get to consume a lot of resources, in which case you have the control to choose -// the caching implementation you like and its setup. -func EnableCustomCache(c Cache) { - cache = c -} - -// EnableCache enables unmarshalling cache with default implementation. More info at EnableCustomCache. -func EnableCache() { - EnableCustomCache(&sync.Map{}) -} - -var cache Cache - -func cacheLookup(t reflect.Type) map[string]reflectionInfo { - if cache == nil { - return nil - } - value, exists := cache.Load(t) - if !exists { - return nil - } - result, _ := value.(map[string]reflectionInfo) - return result -} - -func cacheStore(t reflect.Type, fields map[string]reflectionInfo) { - if cache == nil { - return - } - cache.Store(t, fields) -} diff --git a/vendor/github.com/perimeterx/marshmallow/doc.go b/vendor/github.com/perimeterx/marshmallow/doc.go deleted file mode 100644 index c179e6579..000000000 --- a/vendor/github.com/perimeterx/marshmallow/doc.go +++ /dev/null @@ -1,10 +0,0 @@ -/* -Package marshmallow provides a simple API to perform flexible and performant JSON unmarshalling. -Unlike other packages, marshmallow supports unmarshalling of some known and some unknown fields -with zero performance overhead nor extra coding needed. While unmarshalling, -marshmallow allows fully retaining the original data and access it via a typed struct and a -dynamic map. - -https://github.com/perimeterx/marshmallow -*/ -package marshmallow diff --git a/vendor/github.com/perimeterx/marshmallow/errors.go b/vendor/github.com/perimeterx/marshmallow/errors.go deleted file mode 100644 index c4d341cc5..000000000 --- a/vendor/github.com/perimeterx/marshmallow/errors.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2022 PerimeterX. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package marshmallow - -import ( - "errors" - "fmt" - "github.com/mailru/easyjson/jlexer" - "reflect" - "strings" -) - -var ( - // ErrInvalidInput indicates the input JSON is invalid - ErrInvalidInput = errors.New("invalid JSON input") - - // ErrInvalidValue indicates the target struct has invalid type - ErrInvalidValue = errors.New("unexpected non struct value") -) - -// MultipleLexerError indicates one or more unmarshalling errors during JSON bytes decode -type MultipleLexerError struct { - Errors []*jlexer.LexerError -} - -func (m *MultipleLexerError) Error() string { - errs := make([]string, len(m.Errors)) - for i, lexerError := range m.Errors { - errs[i] = lexerError.Error() - } - return strings.Join(errs, ", ") -} - -// MultipleError indicates one or more unmarshalling errors during JSON map decode -type MultipleError struct { - Errors []error -} - -func (m *MultipleError) Error() string { - errs := make([]string, len(m.Errors)) - for i, lexerError := range m.Errors { - errs[i] = lexerError.Error() - } - return strings.Join(errs, ", ") -} - -// ParseError indicates a JSON map decode error -type ParseError struct { - Reason string - Path string -} - -func (p *ParseError) Error() string { - return fmt.Sprintf("parse error: %s in %s", p.Reason, p.Path) -} - -func newUnexpectedTypeParseError(expectedType reflect.Type, path []string) *ParseError { - return &ParseError{ - Reason: fmt.Sprintf("expected type %s", externalTypeName(expectedType)), - Path: strings.Join(path, "."), - } -} - -func newUnsupportedTypeParseError(unsupportedType reflect.Type, path []string) *ParseError { - return &ParseError{ - Reason: fmt.Sprintf("unsupported type %s", externalTypeName(unsupportedType)), - Path: strings.Join(path, "."), - } -} - -func addUnexpectedTypeLexerError(lexer *jlexer.Lexer, expectedType reflect.Type) { - lexer.AddNonFatalError(fmt.Errorf("expected type %s", externalTypeName(expectedType))) -} - -func addUnsupportedTypeLexerError(lexer *jlexer.Lexer, unsupportedType reflect.Type) { - lexer.AddNonFatalError(fmt.Errorf("unsupported type %s", externalTypeName(unsupportedType))) -} - -func externalTypeName(t reflect.Type) string { - switch t.Kind() { - case reflect.String: - return "string" - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, - reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, - reflect.Float64, reflect.Complex64, reflect.Complex128: - return "number" - case reflect.Bool: - return "boolean" - case reflect.Array, reflect.Slice: - return "array" - case reflect.Interface: - return "any" - case reflect.Map, reflect.Struct: - return "object" - case reflect.Ptr: - return externalTypeName(t.Elem()) - } - return "invalid" -} diff --git a/vendor/github.com/perimeterx/marshmallow/options.go b/vendor/github.com/perimeterx/marshmallow/options.go deleted file mode 100644 index ff97d3369..000000000 --- a/vendor/github.com/perimeterx/marshmallow/options.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2022 PerimeterX. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package marshmallow - -// Mode dictates the unmarshalling mode. -// Each mode is self documented below. -type Mode uint8 - -const ( - // ModeFailOnFirstError is the default mode. It makes unmarshalling terminate - // immediately on any kind of error. This error will then be returned. - ModeFailOnFirstError Mode = iota - - // ModeAllowMultipleErrors mode makes unmarshalling keep decoding even if - // errors are encountered. In case of such error, the erroneous value will be omitted from the result. - // Eventually, all errors will all be returned, alongside the partial result. - ModeAllowMultipleErrors - - // ModeFailOverToOriginalValue mode makes unmarshalling keep decoding even if - // errors are encountered. In case of such error, the original external value be placed in the - // result data, even though it does not meet the schematic requirements. - // Eventually, all errors will be returned, alongside the full result. Note that the result map - // will contain values that do not match the struct schema. - ModeFailOverToOriginalValue -) - -// WithMode is an UnmarshalOption function to set the unmarshalling mode. -func WithMode(mode Mode) UnmarshalOption { - return func(options *unmarshalOptions) { - options.mode = mode - } -} - -// WithSkipPopulateStruct is an UnmarshalOption function to set the skipPopulateStruct option. -// Skipping populate struct is set to false by default. -// If you do not intend to use the struct value once unmarshalling is finished, set this -// option to true to boost performance. This would mean the struct fields will not be set -// with values, but rather it will only be used as the target schema when populating the result map. -func WithSkipPopulateStruct(skipPopulateStruct bool) UnmarshalOption { - return func(options *unmarshalOptions) { - options.skipPopulateStruct = skipPopulateStruct - } -} - -// WithExcludeKnownFieldsFromMap is an UnmarshalOption function to set the excludeKnownFieldsFromMap option. -// Exclude known fields flag is set to false by default. -// When the flag is set to true, fields specified in the input struct (known fields) will be excluded from the result map -func WithExcludeKnownFieldsFromMap(excludeKnownFields bool) UnmarshalOption { - return func(options *unmarshalOptions) { - options.excludeKnownFieldsFromMap = excludeKnownFields - } -} - -type UnmarshalOption func(*unmarshalOptions) - -type unmarshalOptions struct { - mode Mode - skipPopulateStruct bool - excludeKnownFieldsFromMap bool -} - -func buildUnmarshalOptions(options []UnmarshalOption) *unmarshalOptions { - result := &unmarshalOptions{} - for _, option := range options { - option(result) - } - return result -} - -// JSONDataErrorHandler allow types to handle JSON data as maps. -// Types should implement this interface if they wish to act on the map representation of parsed JSON input. -// This is mainly used to allow nested objects to capture unknown fields and leverage marshmallow's abilities. -// If HandleJSONData returns an error, it will be propagated as an unmarshal error -type JSONDataErrorHandler interface { - HandleJSONData(data map[string]interface{}) error -} - -// Deprecated: use JSONDataErrorHandler instead -type JSONDataHandler interface { - HandleJSONData(data map[string]interface{}) -} - -func asJSONDataHandler(value interface{}) (func(map[string]interface{}) error, bool) { - if handler, ok := value.(JSONDataErrorHandler); ok { - return handler.HandleJSONData, true - } - if handler, ok := value.(JSONDataHandler); ok { - return func(m map[string]interface{}) error { - handler.HandleJSONData(m) - return nil - }, true - } - return nil, false -} diff --git a/vendor/github.com/perimeterx/marshmallow/reflection.go b/vendor/github.com/perimeterx/marshmallow/reflection.go deleted file mode 100644 index 9b7d88ce1..000000000 --- a/vendor/github.com/perimeterx/marshmallow/reflection.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2022 PerimeterX. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package marshmallow - -import ( - "encoding/json" - "reflect" - "strings" -) - -var unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() - -type reflectionInfo struct { - path []int - t reflect.Type -} - -func (r reflectionInfo) field(target reflect.Value) reflect.Value { - current := target - for _, i := range r.path { - current = current.Field(i) - } - return current -} - -func mapStructFields(target interface{}) map[string]reflectionInfo { - t := reflectStructType(target) - result := cacheLookup(t) - if result != nil { - return result - } - result = make(map[string]reflectionInfo, t.NumField()) - mapTypeFields(t, result, nil) - cacheStore(t, result) - return result -} - -func mapTypeFields(t reflect.Type, result map[string]reflectionInfo, path []int) { - num := t.NumField() - for i := 0; i < num; i++ { - field := t.Field(i) - fieldPath := append(path, i) - if field.Anonymous && field.Type.Kind() == reflect.Struct { - mapTypeFields(field.Type, result, fieldPath) - continue - } - name := field.Tag.Get("json") - if name == "" || name == "-" { - continue - } - if index := strings.Index(name, ","); index > -1 { - name = name[:index] - } - result[name] = reflectionInfo{ - path: fieldPath, - t: field.Type, - } - } -} - -func reflectStructValue(target interface{}) reflect.Value { - v := reflect.ValueOf(target) - for v.Kind() == reflect.Ptr { - v = v.Elem() - } - return v -} - -func reflectStructType(target interface{}) reflect.Type { - t := reflect.TypeOf(target) - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - return t -} - -var primitiveConverters = map[reflect.Kind]func(v interface{}) (interface{}, bool){ - reflect.Bool: func(v interface{}) (interface{}, bool) { - res, ok := v.(bool) - return res, ok - }, - reflect.Int: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return int(res), true - } - return v, false - }, - reflect.Int8: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return int8(res), true - } - return v, false - }, - reflect.Int16: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return int16(res), true - } - return v, false - }, - reflect.Int32: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return int32(res), true - } - return v, false - }, - reflect.Int64: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return int64(res), true - } - return v, false - }, - reflect.Uint: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return uint(res), true - } - return v, false - }, - reflect.Uint8: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return uint8(res), true - } - return v, false - }, - reflect.Uint16: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return uint16(res), true - } - return v, false - }, - reflect.Uint32: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return uint32(res), true - } - return v, false - }, - reflect.Uint64: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return uint64(res), true - } - return v, false - }, - reflect.Float32: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return float32(res), true - } - return v, false - }, - reflect.Float64: func(v interface{}) (interface{}, bool) { - res, ok := v.(float64) - if ok { - return res, true - } - return v, false - }, - reflect.Interface: func(v interface{}) (interface{}, bool) { - return v, true - }, - reflect.String: func(v interface{}) (interface{}, bool) { - res, ok := v.(string) - return res, ok - }, -} - -func assignValue(field reflect.Value, value interface{}) { - if value == nil { - return - } - reflectValue := reflect.ValueOf(value) - if reflectValue.Type().AssignableTo(field.Type()) { - field.Set(reflectValue) - } -} - -func isValidValue(v interface{}) bool { - value := reflect.ValueOf(v) - return value.Kind() == reflect.Ptr && value.Elem().Kind() == reflect.Struct && !value.IsNil() -} - -func safeReflectValue(t reflect.Type, v interface{}) reflect.Value { - if v == nil { - return reflect.Zero(t) - } - return reflect.ValueOf(v) -} diff --git a/vendor/github.com/perimeterx/marshmallow/unmarshal.go b/vendor/github.com/perimeterx/marshmallow/unmarshal.go deleted file mode 100644 index 160ea30ca..000000000 --- a/vendor/github.com/perimeterx/marshmallow/unmarshal.go +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright 2022 PerimeterX. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package marshmallow - -import ( - "encoding/json" - "github.com/mailru/easyjson/jlexer" - "reflect" -) - -// Unmarshal parses the JSON-encoded object in data and stores the values -// in the struct pointed to by v and in the returned map. -// If v is nil or not a pointer to a struct, Unmarshal returns an ErrInvalidValue. -// If data is not a valid JSON or not a JSON object Unmarshal returns an ErrInvalidInput. -// -// Unmarshal follows the rules of json.Unmarshal with the following exceptions: -// - All input fields are stored in the resulting map, including fields that do not exist in the -// struct pointed by v. -// - Unmarshal only operates on JSON object inputs. It will reject all other types of input -// by returning ErrInvalidInput. -// - Unmarshal only operates on struct values. It will reject all other types of v by -// returning ErrInvalidValue. -// - Unmarshal supports three types of Mode values. Each mode is self documented and affects -// how Unmarshal behaves. -func Unmarshal(data []byte, v interface{}, options ...UnmarshalOption) (map[string]interface{}, error) { - if !isValidValue(v) { - return nil, ErrInvalidValue - } - opts := buildUnmarshalOptions(options) - useMultipleErrors := opts.mode == ModeAllowMultipleErrors || opts.mode == ModeFailOverToOriginalValue - d := &decoder{options: opts, lexer: &jlexer.Lexer{Data: data, UseMultipleErrors: useMultipleErrors}} - result := make(map[string]interface{}) - if d.lexer.IsNull() { - d.lexer.Skip() - } else if !d.lexer.IsDelim('{') { - return nil, ErrInvalidInput - } else { - d.populateStruct(false, v, result) - } - d.lexer.Consumed() - if useMultipleErrors { - errors := d.lexer.GetNonFatalErrors() - if len(errors) == 0 { - return result, nil - } - return result, &MultipleLexerError{Errors: errors} - } - err := d.lexer.Error() - if err != nil { - return nil, err - } - return result, nil -} - -type decoder struct { - options *unmarshalOptions - lexer *jlexer.Lexer -} - -func (d *decoder) populateStruct(forcePopulate bool, structInstance interface{}, result map[string]interface{}) (interface{}, bool) { - doPopulate := !d.options.skipPopulateStruct || forcePopulate - var structValue reflect.Value - if doPopulate { - structValue = reflectStructValue(structInstance) - } - fields := mapStructFields(structInstance) - var clone map[string]interface{} - if d.options.mode == ModeFailOverToOriginalValue { - clone = make(map[string]interface{}, len(fields)) - } - d.lexer.Delim('{') - for !d.lexer.IsDelim('}') { - key := d.lexer.UnsafeFieldName(false) - d.lexer.WantColon() - refInfo, exists := fields[key] - if exists { - value, isValidType := d.valueByReflectType(refInfo.t) - if isValidType { - if value != nil && doPopulate { - field := refInfo.field(structValue) - assignValue(field, value) - } - if !d.options.excludeKnownFieldsFromMap { - if result != nil { - result[key] = value - } - if clone != nil { - clone[key] = value - } - } - } else { - switch d.options.mode { - case ModeFailOnFirstError: - return nil, false - case ModeFailOverToOriginalValue: - if !forcePopulate { - result[key] = value - } else { - clone[key] = value - d.lexer.WantComma() - d.drainLexerMap(clone) - return clone, false - } - } - } - } else { - value := d.lexer.Interface() - if result != nil { - result[key] = value - } - if clone != nil { - clone[key] = value - } - } - d.lexer.WantComma() - } - d.lexer.Delim('}') - return structInstance, true -} - -func (d *decoder) valueByReflectType(t reflect.Type) (interface{}, bool) { - if t.Implements(unmarshalerType) { - result := reflect.New(t.Elem()).Interface() - d.valueFromCustomUnmarshaler(result.(json.Unmarshaler)) - return result, true - } - if reflect.PtrTo(t).Implements(unmarshalerType) { - value := reflect.New(t) - d.valueFromCustomUnmarshaler(value.Interface().(json.Unmarshaler)) - return value.Elem().Interface(), true - } - kind := t.Kind() - if converter := primitiveConverters[kind]; converter != nil { - v := d.lexer.Interface() - if v == nil { - return nil, true - } - converted, ok := converter(v) - if !ok { - addUnexpectedTypeLexerError(d.lexer, t) - return v, false - } - return converted, true - } - switch kind { - case reflect.Slice: - return d.buildSlice(t) - case reflect.Array: - return d.buildArray(t) - case reflect.Map: - return d.buildMap(t) - case reflect.Struct: - value, valid := d.buildStruct(t) - if value == nil { - return nil, valid - } - if !valid { - return value, false - } - return reflect.ValueOf(value).Elem().Interface(), valid - case reflect.Ptr: - if t.Elem().Kind() == reflect.Struct { - return d.buildStruct(t.Elem()) - } - value, valid := d.valueByReflectType(t.Elem()) - if value == nil { - return nil, valid - } - if !valid { - return value, false - } - result := reflect.New(reflect.TypeOf(value)) - result.Elem().Set(reflect.ValueOf(value)) - return result.Interface(), valid - } - addUnsupportedTypeLexerError(d.lexer, t) - return nil, false -} - -func (d *decoder) buildSlice(sliceType reflect.Type) (interface{}, bool) { - if d.lexer.IsNull() { - d.lexer.Skip() - return nil, true - } - if !d.lexer.IsDelim('[') { - addUnexpectedTypeLexerError(d.lexer, sliceType) - return d.lexer.Interface(), false - } - elemType := sliceType.Elem() - d.lexer.Delim('[') - var sliceValue reflect.Value - if !d.lexer.IsDelim(']') { - sliceValue = reflect.MakeSlice(sliceType, 0, 4) - } else { - sliceValue = reflect.MakeSlice(sliceType, 0, 0) - } - for !d.lexer.IsDelim(']') { - current, valid := d.valueByReflectType(elemType) - if !valid { - if d.options.mode != ModeFailOverToOriginalValue { - d.drainLexerArray(nil) - return nil, true - } - result := d.cloneReflectArray(sliceValue, -1) - result = append(result, current) - return d.drainLexerArray(result), true - } - sliceValue = reflect.Append(sliceValue, safeReflectValue(elemType, current)) - d.lexer.WantComma() - } - d.lexer.Delim(']') - return sliceValue.Interface(), true -} - -func (d *decoder) buildArray(arrayType reflect.Type) (interface{}, bool) { - if d.lexer.IsNull() { - d.lexer.Skip() - return nil, true - } - if !d.lexer.IsDelim('[') { - addUnexpectedTypeLexerError(d.lexer, arrayType) - return d.lexer.Interface(), false - } - elemType := arrayType.Elem() - arrayValue := reflect.New(arrayType).Elem() - d.lexer.Delim('[') - for i := 0; !d.lexer.IsDelim(']'); i++ { - current, valid := d.valueByReflectType(elemType) - if !valid { - if d.options.mode != ModeFailOverToOriginalValue { - d.drainLexerArray(nil) - return nil, true - } - result := d.cloneReflectArray(arrayValue, i) - result = append(result, current) - return d.drainLexerArray(result), true - } - if current != nil { - arrayValue.Index(i).Set(reflect.ValueOf(current)) - } - d.lexer.WantComma() - } - d.lexer.Delim(']') - return arrayValue.Interface(), true -} - -func (d *decoder) buildMap(mapType reflect.Type) (interface{}, bool) { - if d.lexer.IsNull() { - d.lexer.Skip() - return nil, true - } - if !d.lexer.IsDelim('{') { - addUnexpectedTypeLexerError(d.lexer, mapType) - return d.lexer.Interface(), false - } - d.lexer.Delim('{') - keyType := mapType.Key() - valueType := mapType.Elem() - mapValue := reflect.MakeMap(mapType) - for !d.lexer.IsDelim('}') { - key, valid := d.valueByReflectType(keyType) - if !valid { - if d.options.mode != ModeFailOverToOriginalValue { - d.lexer.WantColon() - d.lexer.Interface() - d.lexer.WantComma() - d.drainLexerMap(make(map[string]interface{})) - return nil, true - } - strKey, _ := key.(string) - d.lexer.WantColon() - value := d.lexer.Interface() - result := d.cloneReflectMap(mapValue) - result[strKey] = value - d.lexer.WantComma() - d.drainLexerMap(result) - return result, true - } - d.lexer.WantColon() - value, valid := d.valueByReflectType(valueType) - if !valid { - if d.options.mode != ModeFailOverToOriginalValue { - d.lexer.WantComma() - d.drainLexerMap(make(map[string]interface{})) - return nil, true - } - strKey, _ := key.(string) - result := d.cloneReflectMap(mapValue) - result[strKey] = value - d.lexer.WantComma() - d.drainLexerMap(result) - return result, true - } - mapValue.SetMapIndex(safeReflectValue(keyType, key), safeReflectValue(valueType, value)) - d.lexer.WantComma() - } - d.lexer.Delim('}') - return mapValue.Interface(), true -} - -func (d *decoder) buildStruct(structType reflect.Type) (interface{}, bool) { - if d.lexer.IsNull() { - d.lexer.Skip() - return nil, true - } - if !d.lexer.IsDelim('{') { - addUnexpectedTypeLexerError(d.lexer, structType) - return d.lexer.Interface(), false - } - value := reflect.New(structType).Interface() - handler, ok := asJSONDataHandler(value) - if !ok { - return d.populateStruct(true, value, nil) - } - data := make(map[string]interface{}) - result, valid := d.populateStruct(true, value, data) - if !valid { - return result, false - } - err := handler(data) - if err != nil { - d.lexer.AddNonFatalError(err) - return result, false - } - return result, true -} - -func (d *decoder) valueFromCustomUnmarshaler(unmarshaler json.Unmarshaler) { - data := d.lexer.Raw() - if !d.lexer.Ok() { - return - } - err := unmarshaler.UnmarshalJSON(data) - if err != nil { - d.lexer.AddNonFatalError(err) - } -} - -func (d *decoder) cloneReflectArray(value reflect.Value, length int) []interface{} { - if length == -1 { - length = value.Len() - } - result := make([]interface{}, length) - for i := 0; i < length; i++ { - result[i] = value.Index(i).Interface() - } - return result -} - -func (d *decoder) cloneReflectMap(mapValue reflect.Value) map[string]interface{} { - l := mapValue.Len() - result := make(map[string]interface{}, l) - for _, key := range mapValue.MapKeys() { - value := mapValue.MapIndex(key) - strKey, _ := key.Interface().(string) - result[strKey] = value.Interface() - } - return result -} - -func (d *decoder) drainLexerArray(target []interface{}) interface{} { - d.lexer.WantComma() - for !d.lexer.IsDelim(']') { - current := d.lexer.Interface() - target = append(target, current) - d.lexer.WantComma() - } - d.lexer.Delim(']') - return target -} - -func (d *decoder) drainLexerMap(target map[string]interface{}) { - for !d.lexer.IsDelim('}') { - key := d.lexer.String() - d.lexer.WantColon() - value := d.lexer.Interface() - target[key] = value - d.lexer.WantComma() - } - d.lexer.Delim('}') -} diff --git a/vendor/github.com/perimeterx/marshmallow/unmarshal_from_json_map.go b/vendor/github.com/perimeterx/marshmallow/unmarshal_from_json_map.go deleted file mode 100644 index 0907f8f87..000000000 --- a/vendor/github.com/perimeterx/marshmallow/unmarshal_from_json_map.go +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright 2022 PerimeterX. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package marshmallow - -import ( - "reflect" -) - -// UnmarshalerFromJSONMap is the interface implemented by types -// that can unmarshal a JSON description of themselves. -// In case you want to implement custom unmarshalling, json.Unmarshaler only supports -// receiving the data as []byte. However, while unmarshalling from JSON map, -// the data is not available as a raw []byte and converting to it will significantly -// hurt performance. Thus, if you wish to implement a custom unmarshalling on a type -// that is being unmarshalled from a JSON map, you need to implement -// UnmarshalerFromJSONMap interface. -type UnmarshalerFromJSONMap interface { - UnmarshalJSONFromMap(data interface{}) error -} - -// UnmarshalFromJSONMap parses the JSON map data and stores the values -// in the struct pointed to by v and in the returned map. -// If v is nil or not a pointer to a struct, UnmarshalFromJSONMap returns an ErrInvalidValue. -// -// UnmarshalFromJSONMap follows the rules of json.Unmarshal with the following exceptions: -// - All input fields are stored in the resulting map, including fields that do not exist in the -// struct pointed by v. -// - UnmarshalFromJSONMap receive a JSON map instead of raw bytes. The given input map is assumed -// to be a JSON map, meaning it should only contain the following types: bool, string, float64, -// []interface, and map[string]interface{}. Other types will cause decoding to return unexpected results. -// - UnmarshalFromJSONMap only operates on struct values. It will reject all other types of v by -// returning ErrInvalidValue. -// - UnmarshalFromJSONMap supports three types of Mode values. Each mode is self documented and affects -// how UnmarshalFromJSONMap behaves. -func UnmarshalFromJSONMap(data map[string]interface{}, v interface{}, options ...UnmarshalOption) (map[string]interface{}, error) { - if !isValidValue(v) { - return nil, ErrInvalidValue - } - opts := buildUnmarshalOptions(options) - d := &mapDecoder{options: opts} - result := make(map[string]interface{}) - if data != nil { - d.populateStruct(false, nil, data, v, result) - } - if opts.mode == ModeAllowMultipleErrors || opts.mode == ModeFailOverToOriginalValue { - if len(d.errs) == 0 { - return result, nil - } - return result, &MultipleError{Errors: d.errs} - } - if d.err != nil { - return nil, d.err - } - return result, nil -} - -var unmarshalerFromJSONMapType = reflect.TypeOf((*UnmarshalerFromJSONMap)(nil)).Elem() - -type mapDecoder struct { - options *unmarshalOptions - err error - errs []error -} - -func (m *mapDecoder) populateStruct(forcePopulate bool, path []string, data map[string]interface{}, structInstance interface{}, result map[string]interface{}) (interface{}, bool) { - doPopulate := !m.options.skipPopulateStruct || forcePopulate - var structValue reflect.Value - if doPopulate { - structValue = reflectStructValue(structInstance) - } - fields := mapStructFields(structInstance) - for key, inputValue := range data { - refInfo, exists := fields[key] - if exists { - value, isValidType := m.valueByReflectType(append(path, key), inputValue, refInfo.t) - if isValidType { - if value != nil && doPopulate { - field := refInfo.field(structValue) - assignValue(field, value) - } - if !m.options.excludeKnownFieldsFromMap { - if result != nil { - result[key] = value - } - } - } else { - switch m.options.mode { - case ModeFailOnFirstError: - return nil, false - case ModeFailOverToOriginalValue: - if !forcePopulate { - result[key] = value - } else { - return data, false - } - } - } - } else { - if result != nil { - result[key] = inputValue - } - } - } - return structInstance, true -} - -func (m *mapDecoder) valueByReflectType(path []string, v interface{}, t reflect.Type) (interface{}, bool) { - if t.Implements(unmarshalerFromJSONMapType) { - result := reflect.New(t.Elem()).Interface() - m.valueFromCustomUnmarshaler(v, result.(UnmarshalerFromJSONMap)) - return result, true - } - if reflect.PtrTo(t).Implements(unmarshalerFromJSONMapType) { - value := reflect.New(t) - m.valueFromCustomUnmarshaler(v, value.Interface().(UnmarshalerFromJSONMap)) - return value.Elem().Interface(), true - } - kind := t.Kind() - if converter := primitiveConverters[kind]; converter != nil { - if v == nil { - return nil, true - } - converted, ok := converter(v) - if !ok { - m.addError(newUnexpectedTypeParseError(t, path)) - return v, false - } - return converted, true - } - switch kind { - case reflect.Slice: - return m.buildSlice(path, v, t) - case reflect.Array: - return m.buildArray(path, v, t) - case reflect.Map: - return m.buildMap(path, v, t) - case reflect.Struct: - value, valid := m.buildStruct(path, v, t) - if value == nil { - return nil, valid - } - if !valid { - return value, false - } - return reflect.ValueOf(value).Elem().Interface(), valid - case reflect.Ptr: - if t.Elem().Kind() == reflect.Struct { - return m.buildStruct(path, v, t.Elem()) - } - value, valid := m.valueByReflectType(path, v, t.Elem()) - if value == nil { - return nil, valid - } - if !valid { - return value, false - } - result := reflect.New(reflect.TypeOf(value)) - result.Elem().Set(reflect.ValueOf(value)) - return result.Interface(), valid - } - m.addError(newUnsupportedTypeParseError(t, path)) - return nil, false -} - -func (m *mapDecoder) buildSlice(path []string, v interface{}, sliceType reflect.Type) (interface{}, bool) { - if v == nil { - return nil, true - } - arr, ok := v.([]interface{}) - if !ok { - m.addError(newUnexpectedTypeParseError(sliceType, path)) - return v, false - } - elemType := sliceType.Elem() - var sliceValue reflect.Value - if len(arr) > 0 { - sliceValue = reflect.MakeSlice(sliceType, 0, 4) - } else { - sliceValue = reflect.MakeSlice(sliceType, 0, 0) - } - for _, element := range arr { - current, valid := m.valueByReflectType(path, element, elemType) - if !valid { - if m.options.mode != ModeFailOverToOriginalValue { - return nil, true - } - return v, true - } - sliceValue = reflect.Append(sliceValue, safeReflectValue(elemType, current)) - } - return sliceValue.Interface(), true -} - -func (m *mapDecoder) buildArray(path []string, v interface{}, arrayType reflect.Type) (interface{}, bool) { - if v == nil { - return nil, true - } - arr, ok := v.([]interface{}) - if !ok { - m.addError(newUnexpectedTypeParseError(arrayType, path)) - return v, false - } - elemType := arrayType.Elem() - arrayValue := reflect.New(arrayType).Elem() - for i, element := range arr { - current, valid := m.valueByReflectType(path, element, elemType) - if !valid { - if m.options.mode != ModeFailOverToOriginalValue { - return nil, true - } - return v, true - } - if current != nil { - arrayValue.Index(i).Set(reflect.ValueOf(current)) - } - } - return arrayValue.Interface(), true -} - -func (m *mapDecoder) buildMap(path []string, v interface{}, mapType reflect.Type) (interface{}, bool) { - if v == nil { - return nil, true - } - mp, ok := v.(map[string]interface{}) - if !ok { - m.addError(newUnexpectedTypeParseError(mapType, path)) - return v, false - } - keyType := mapType.Key() - valueType := mapType.Elem() - mapValue := reflect.MakeMap(mapType) - for inputKey, inputValue := range mp { - keyPath := append(path, inputKey) - key, valid := m.valueByReflectType(keyPath, inputKey, keyType) - if !valid { - if m.options.mode != ModeFailOverToOriginalValue { - return nil, true - } - return v, true - } - value, valid := m.valueByReflectType(keyPath, inputValue, valueType) - if !valid { - if m.options.mode != ModeFailOverToOriginalValue { - return nil, true - } - return v, true - } - mapValue.SetMapIndex(safeReflectValue(keyType, key), safeReflectValue(valueType, value)) - } - return mapValue.Interface(), true -} - -func (m *mapDecoder) buildStruct(path []string, v interface{}, structType reflect.Type) (interface{}, bool) { - if v == nil { - return nil, true - } - mp, ok := v.(map[string]interface{}) - if !ok { - m.addError(newUnexpectedTypeParseError(structType, path)) - return v, false - } - value := reflect.New(structType).Interface() - handler, ok := asJSONDataHandler(value) - if !ok { - return m.populateStruct(true, path, mp, value, nil) - } - data := make(map[string]interface{}) - result, valid := m.populateStruct(true, path, mp, value, data) - if !valid { - return result, false - } - err := handler(data) - if err != nil { - m.addError(err) - return result, false - } - return result, true -} - -func (m *mapDecoder) valueFromCustomUnmarshaler(data interface{}, unmarshaler UnmarshalerFromJSONMap) { - err := unmarshaler.UnmarshalJSONFromMap(data) - if err != nil { - m.addError(err) - } -} - -func (m *mapDecoder) addError(err error) { - if m.options.mode == ModeFailOnFirstError { - m.err = err - } else { - m.errs = append(m.errs, err) - } -} diff --git a/vendor/github.com/rogpeppe/go-internal/fmtsort/sort.go b/vendor/github.com/rogpeppe/go-internal/fmtsort/sort.go index 7f5185417..6af8c7f7b 100644 --- a/vendor/github.com/rogpeppe/go-internal/fmtsort/sort.go +++ b/vendor/github.com/rogpeppe/go-internal/fmtsort/sort.go @@ -143,14 +143,14 @@ func compare(aVal, bVal reflect.Value) int { return 0 } case reflect.Struct: - for i := 0; i < aVal.NumField(); i++ { + for i := range aVal.NumField() { if c := compare(aVal.Field(i), bVal.Field(i)); c != 0 { return c } } return 0 case reflect.Array: - for i := 0; i < aVal.Len(); i++ { + for i := range aVal.Len() { if c := compare(aVal.Index(i), bVal.Index(i)); c != 0 { return c } diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/.gitmodules b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.gitmodules new file mode 100644 index 000000000..d14f5ea70 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.gitmodules @@ -0,0 +1,4 @@ +[submodule "testdata/JSON-Schema-Test-Suite"] + path = testdata/JSON-Schema-Test-Suite + url = https://github.com/json-schema-org/JSON-Schema-Test-Suite.git + branch = main diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/.golangci.yml b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.golangci.yml new file mode 100644 index 000000000..6534d5316 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.golangci.yml @@ -0,0 +1,7 @@ +version: "2" +linters: + enable: + - nakedret + - errname + - godot + - misspell diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/.pre-commit-hooks.yaml b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.pre-commit-hooks.yaml new file mode 100644 index 000000000..695b502ed --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.pre-commit-hooks.yaml @@ -0,0 +1,7 @@ +- id: jsonschema-validate + name: Validate JSON against JSON Schema + description: ensure json files follow specified JSON Schema + entry: jv + language: golang + additional_dependencies: + - ./cmd/jv diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/.swp b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.swp new file mode 100644 index 000000000..e51190031 Binary files /dev/null and b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.swp differ diff --git a/vendor/gopkg.in/yaml.v2/LICENSE b/vendor/github.com/santhosh-tekuri/jsonschema/v6/LICENSE similarity index 89% rename from vendor/gopkg.in/yaml.v2/LICENSE rename to vendor/github.com/santhosh-tekuri/jsonschema/v6/LICENSE index 8dada3eda..19dc35b24 100644 --- a/vendor/gopkg.in/yaml.v2/LICENSE +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/LICENSE @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -171,31 +172,4 @@ of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/README.md b/vendor/github.com/santhosh-tekuri/jsonschema/v6/README.md new file mode 100644 index 000000000..1243b66c5 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/README.md @@ -0,0 +1,88 @@ +# jsonschema v6.0.2 + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![GoDoc](https://godoc.org/github.com/santhosh-tekuri/jsonschema?status.svg)](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v6) +[![Go Report Card](https://goreportcard.com/badge/github.com/santhosh-tekuri/jsonschema/v6)](https://goreportcard.com/report/github.com/santhosh-tekuri/jsonschema/v6) +[![Build Status](https://github.com/santhosh-tekuri/jsonschema/actions/workflows/go.yaml/badge.svg?branch=boon)](https://github.com/santhosh-tekuri/jsonschema/actions/workflows/go.yaml) +[![codecov](https://codecov.io/gh/santhosh-tekuri/jsonschema/branch/boon/graph/badge.svg?token=JMVj1pFT2l)](https://codecov.io/gh/santhosh-tekuri/jsonschema/tree/boon) + +see [godoc](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v6) for examples + +## Library Features + +- [x] pass [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite) excluding optional(compare with other impls at [bowtie](https://bowtie-json-schema.github.io/bowtie/#)) + - [x] [![draft-04](https://img.shields.io/endpoint?url=https://bowtie.report/badges/go-jsonschema/compliance/draft4.json)](https://bowtie.report/#/dialects/draft4) + - [x] [![draft-06](https://img.shields.io/endpoint?url=https://bowtie.report/badges/go-jsonschema/compliance/draft6.json)](https://bowtie.report/#/dialects/draft6) + - [x] [![draft-07](https://img.shields.io/endpoint?url=https://bowtie.report/badges/go-jsonschema/compliance/draft7.json)](https://bowtie.report/#/dialects/draft7) + - [x] [![draft/2019-09](https://img.shields.io/endpoint?url=https://bowtie.report/badges/go-jsonschema/compliance/draft2019-09.json)](https://bowtie.report/#/dialects/draft2019-09) + - [x] [![draft/2020-12](https://img.shields.io/endpoint?url=https://bowtie.report/badges/go-jsonschema/compliance/draft2020-12.json)](https://bowtie.report/#/dialects/draft2020-12) +- [x] detect infinite loop traps + - [x] `$schema` cycle + - [x] validation cycle +- [x] custom `$schema` url +- [x] vocabulary based validation +- [x] custom regex engine +- [x] format assertions + - [x] flag to enable in draft >= 2019-09 + - [x] custom format registration + - [x] built-in formats + - [x] regex, uuid + - [x] ipv4, ipv6 + - [x] hostname, email + - [x] date, time, date-time, duration + - [x] json-pointer, relative-json-pointer + - [x] uri, uri-reference, uri-template + - [x] iri, iri-reference + - [x] period, semver +- [x] content assertions + - [x] flag to enable in draft >= 7 + - [x] contentEncoding + - [x] base64 + - [x] custom + - [x] contentMediaType + - [x] application/json + - [x] custom + - [x] contentSchema +- [x] errors + - [x] introspectable + - [x] hierarchy + - [x] alternative display with `#` + - [x] output + - [x] flag + - [x] basic + - [x] detailed +- [x] custom vocabulary + - enable via `$vocabulary` for draft >=2019-19 + - enable via flag for draft <= 7 +- [x] mixed dialect support + +## CLI v0.7.0 + +to install: `go install github.com/santhosh-tekuri/jsonschema/cmd/jv@latest` + +Note that the cli is versioned independently. you can see it in git tags `cmd/jv/v0.7.0` + +``` +Usage: jv [OPTIONS] SCHEMA [INSTANCE...] + +Options: + -c, --assert-content Enable content assertions with draft >= 7 + -f, --assert-format Enable format assertions with draft >= 2019 + --cacert pem-file Use the specified pem-file to verify the peer. The file may contain multiple CA certificates + -d, --draft version Draft version used when '$schema' is missing. Valid values 4, 6, 7, 2019, 2020 (default 2020) + -h, --help Print help information + -k, --insecure Use insecure TLS connection + -o, --output format Output format. Valid values simple, alt, flag, basic, detailed (default "simple") + -q, --quiet Do not print errors + -v, --version Print build information +``` + +- [x] exit code `1` for validation errors, `2` for usage errors +- [x] validate both schema and multiple instances +- [x] support both json and yaml files +- [x] support standard input, use `-` +- [x] quite mode with parsable output +- [x] http(s) url support + - [x] custom certs for validation, use `--cacert` + - [x] flag to skip certificate verification, use `--insecure` + diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/compiler.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/compiler.go new file mode 100644 index 000000000..4da736103 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/compiler.go @@ -0,0 +1,332 @@ +package jsonschema + +import ( + "fmt" + "regexp" + "slices" +) + +// Compiler compiles json schema into *Schema. +type Compiler struct { + schemas map[urlPtr]*Schema + roots *roots + formats map[string]*Format + decoders map[string]*Decoder + mediaTypes map[string]*MediaType + assertFormat bool + assertContent bool +} + +// NewCompiler create Compiler Object. +func NewCompiler() *Compiler { + return &Compiler{ + schemas: map[urlPtr]*Schema{}, + roots: newRoots(), + formats: map[string]*Format{}, + decoders: map[string]*Decoder{}, + mediaTypes: map[string]*MediaType{}, + assertFormat: false, + assertContent: false, + } +} + +// DefaultDraft overrides the draft used to +// compile schemas without `$schema` field. +// +// By default, this library uses the latest +// draft supported. +// +// The use of this option is HIGHLY encouraged +// to ensure continued correct operation of your +// schema. The current default value will not stay +// the same overtime. +func (c *Compiler) DefaultDraft(d *Draft) { + c.roots.defaultDraft = d +} + +// AssertFormat always enables format assertions. +// +// Default Behavior: +// for draft-07: enabled. +// for draft/2019-09: disabled unless metaschema says `format` vocabulary is required. +// for draft/2020-12: disabled unless metaschema says `format-assertion` vocabulary is required. +func (c *Compiler) AssertFormat() { + c.assertFormat = true +} + +// AssertContent enables content assertions. +// +// Content assertions include keywords: +// - contentEncoding +// - contentMediaType +// - contentSchema +// +// Default behavior is always disabled. +func (c *Compiler) AssertContent() { + c.assertContent = true +} + +// RegisterFormat registers custom format. +// +// NOTE: +// - "regex" format can not be overridden +// - format assertions are disabled for draft >= 2019-09 +// see [Compiler.AssertFormat] +func (c *Compiler) RegisterFormat(f *Format) { + if f.Name != "regex" { + c.formats[f.Name] = f + } +} + +// RegisterContentEncoding registers custom contentEncoding. +// +// NOTE: content assertions are disabled by default. +// see [Compiler.AssertContent]. +func (c *Compiler) RegisterContentEncoding(d *Decoder) { + c.decoders[d.Name] = d +} + +// RegisterContentMediaType registers custom contentMediaType. +// +// NOTE: content assertions are disabled by default. +// see [Compiler.AssertContent]. +func (c *Compiler) RegisterContentMediaType(mt *MediaType) { + c.mediaTypes[mt.Name] = mt +} + +// RegisterVocabulary registers custom vocabulary. +// +// NOTE: +// - vocabularies are disabled for draft >= 2019-09 +// see [Compiler.AssertVocabs] +func (c *Compiler) RegisterVocabulary(vocab *Vocabulary) { + c.roots.vocabularies[vocab.URL] = vocab +} + +// AssertVocabs always enables user-defined vocabularies assertions. +// +// Default Behavior: +// for draft-07: enabled. +// for draft/2019-09: disabled unless metaschema enables a vocabulary. +// for draft/2020-12: disabled unless metaschema enables a vocabulary. +func (c *Compiler) AssertVocabs() { + c.roots.assertVocabs = true +} + +// AddResource adds schema resource which gets used later in reference +// resolution. +// +// The argument url can be file path or url. Any fragment in url is ignored. +// The argument doc must be valid json value. +func (c *Compiler) AddResource(url string, doc any) error { + uf, err := absolute(url) + if err != nil { + return err + } + if isMeta(string(uf.url)) { + return &ResourceExistsError{string(uf.url)} + } + if !c.roots.loader.add(uf.url, doc) { + return &ResourceExistsError{string(uf.url)} + } + return nil +} + +// UseLoader overrides the default [URLLoader] used +// to load schema resources. +func (c *Compiler) UseLoader(loader URLLoader) { + c.roots.loader.loader = loader +} + +// UseRegexpEngine changes the regexp-engine used. +// By default it uses regexp package from go standard +// library. +// +// NOTE: must be called before compiling any schemas. +func (c *Compiler) UseRegexpEngine(engine RegexpEngine) { + if engine == nil { + engine = goRegexpCompile + } + c.roots.regexpEngine = engine +} + +func (c *Compiler) enqueue(q *queue, up urlPtr) *Schema { + if sch, ok := c.schemas[up]; ok { + // already got compiled + return sch + } + if sch := q.get(up); sch != nil { + return sch + } + sch := newSchema(up) + q.append(sch) + return sch +} + +// MustCompile is like [Compile] but panics if compilation fails. +// It simplifies safe initialization of global variables holding +// compiled schema. +func (c *Compiler) MustCompile(loc string) *Schema { + sch, err := c.Compile(loc) + if err != nil { + panic(fmt.Sprintf("jsonschema: Compile(%q): %v", loc, err)) + } + return sch +} + +// Compile compiles json-schema at given loc. +func (c *Compiler) Compile(loc string) (*Schema, error) { + uf, err := absolute(loc) + if err != nil { + return nil, err + } + up, err := c.roots.resolveFragment(*uf) + if err != nil { + return nil, err + } + return c.doCompile(up) +} + +func (c *Compiler) doCompile(up urlPtr) (*Schema, error) { + q := &queue{} + compiled := 0 + + c.enqueue(q, up) + for q.len() > compiled { + sch := q.at(compiled) + if err := c.roots.ensureSubschema(sch.up); err != nil { + return nil, err + } + r := c.roots.roots[sch.up.url] + v, err := sch.up.lookup(r.doc) + if err != nil { + return nil, err + } + if err := c.compileValue(v, sch, r, q); err != nil { + return nil, err + } + compiled++ + } + for _, sch := range *q { + c.schemas[sch.up] = sch + } + return c.schemas[up], nil +} + +func (c *Compiler) compileValue(v any, sch *Schema, r *root, q *queue) error { + res := r.resource(sch.up.ptr) + sch.DraftVersion = res.dialect.draft.version + + base := urlPtr{sch.up.url, res.ptr} + sch.resource = c.enqueue(q, base) + + // if resource, enqueue dynamic anchors for compilation + if sch.DraftVersion >= 2020 && sch.up == sch.resource.up { + res := r.resource(sch.up.ptr) + for anchor, anchorPtr := range res.anchors { + if slices.Contains(res.dynamicAnchors, anchor) { + up := urlPtr{sch.up.url, anchorPtr} + danchorSch := c.enqueue(q, up) + if sch.dynamicAnchors == nil { + sch.dynamicAnchors = map[string]*Schema{} + } + sch.dynamicAnchors[string(anchor)] = danchorSch + } + } + } + + switch v := v.(type) { + case bool: + sch.Bool = &v + case map[string]any: + if err := c.compileObject(v, sch, r, q); err != nil { + return err + } + } + + sch.allPropsEvaluated = sch.AdditionalProperties != nil + if sch.DraftVersion < 2020 { + sch.allItemsEvaluated = sch.AdditionalItems != nil + switch items := sch.Items.(type) { + case *Schema: + sch.allItemsEvaluated = true + case []*Schema: + sch.numItemsEvaluated = len(items) + } + } else { + sch.allItemsEvaluated = sch.Items2020 != nil + sch.numItemsEvaluated = len(sch.PrefixItems) + } + + return nil +} + +func (c *Compiler) compileObject(obj map[string]any, sch *Schema, r *root, q *queue) error { + if len(obj) == 0 { + b := true + sch.Bool = &b + return nil + } + oc := objCompiler{ + c: c, + obj: obj, + up: sch.up, + r: r, + res: r.resource(sch.up.ptr), + q: q, + } + return oc.compile(sch) +} + +// queue -- + +type queue []*Schema + +func (q *queue) append(sch *Schema) { + *q = append(*q, sch) +} + +func (q *queue) at(i int) *Schema { + return (*q)[i] +} + +func (q *queue) len() int { + return len(*q) +} + +func (q *queue) get(up urlPtr) *Schema { + i := slices.IndexFunc(*q, func(sch *Schema) bool { return sch.up == up }) + if i != -1 { + return (*q)[i] + } + return nil +} + +// regexp -- + +// Regexp is the representation of compiled regular expression. +type Regexp interface { + fmt.Stringer + + // MatchString reports whether the string s contains + // any match of the regular expression. + MatchString(string) bool +} + +// RegexpEngine parses a regular expression and returns, +// if successful, a Regexp object that can be used to +// match against text. +type RegexpEngine func(string) (Regexp, error) + +func (re RegexpEngine) validate(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + _, err := re(s) + return err +} + +func goRegexpCompile(s string) (Regexp, error) { + return regexp.Compile(s) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/content.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/content.go new file mode 100644 index 000000000..8d62e58b0 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/content.go @@ -0,0 +1,51 @@ +package jsonschema + +import ( + "bytes" + "encoding/base64" + "encoding/json" +) + +// Decoder specifies how to decode specific contentEncoding. +type Decoder struct { + // Name of contentEncoding. + Name string + // Decode given string to byte array. + Decode func(string) ([]byte, error) +} + +var decoders = map[string]*Decoder{ + "base64": { + Name: "base64", + Decode: func(s string) ([]byte, error) { + return base64.StdEncoding.DecodeString(s) + }, + }, +} + +// MediaType specified how to validate bytes against specific contentMediaType. +type MediaType struct { + // Name of contentMediaType. + Name string + + // Validate checks whether bytes conform to this mediatype. + Validate func([]byte) error + + // UnmarshalJSON unmarshals bytes into json value. + // This must be nil if this mediatype is not compatible + // with json. + UnmarshalJSON func([]byte) (any, error) +} + +var mediaTypes = map[string]*MediaType{ + "application/json": { + Name: "application/json", + Validate: func(b []byte) error { + var v any + return json.Unmarshal(b, &v) + }, + UnmarshalJSON: func(b []byte) (any, error) { + return UnmarshalJSON(bytes.NewReader(b)) + }, + }, +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/draft.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/draft.go new file mode 100644 index 000000000..fd09bae8d --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/draft.go @@ -0,0 +1,360 @@ +package jsonschema + +import ( + "fmt" + "slices" + "strings" +) + +// A Draft represents json-schema specification. +type Draft struct { + version int + url string + sch *Schema + id string // property name used to represent id + subschemas []SchemaPath // locations of subschemas + vocabPrefix string // prefix used for vocabulary + allVocabs map[string]*Schema // names of supported vocabs with its schemas + defaultVocabs []string // names of default vocabs +} + +// String returns the specification url. +func (d *Draft) String() string { + return d.url +} + +var ( + Draft4 = &Draft{ + version: 4, + url: "http://json-schema.org/draft-04/schema", + id: "id", + subschemas: []SchemaPath{ + // type agonistic + schemaPath("definitions/*"), + schemaPath("not"), + schemaPath("allOf/[]"), + schemaPath("anyOf/[]"), + schemaPath("oneOf/[]"), + // object + schemaPath("properties/*"), + schemaPath("additionalProperties"), + schemaPath("patternProperties/*"), + // array + schemaPath("items"), + schemaPath("items/[]"), + schemaPath("additionalItems"), + schemaPath("dependencies/*"), + }, + vocabPrefix: "", + allVocabs: map[string]*Schema{}, + defaultVocabs: []string{}, + } + + Draft6 = &Draft{ + version: 6, + url: "http://json-schema.org/draft-06/schema", + id: "$id", + subschemas: joinSubschemas(Draft4.subschemas, + schemaPath("propertyNames"), + schemaPath("contains"), + ), + vocabPrefix: "", + allVocabs: map[string]*Schema{}, + defaultVocabs: []string{}, + } + + Draft7 = &Draft{ + version: 7, + url: "http://json-schema.org/draft-07/schema", + id: "$id", + subschemas: joinSubschemas(Draft6.subschemas, + schemaPath("if"), + schemaPath("then"), + schemaPath("else"), + ), + vocabPrefix: "", + allVocabs: map[string]*Schema{}, + defaultVocabs: []string{}, + } + + Draft2019 = &Draft{ + version: 2019, + url: "https://json-schema.org/draft/2019-09/schema", + id: "$id", + subschemas: joinSubschemas(Draft7.subschemas, + schemaPath("$defs/*"), + schemaPath("dependentSchemas/*"), + schemaPath("unevaluatedProperties"), + schemaPath("unevaluatedItems"), + schemaPath("contentSchema"), + ), + vocabPrefix: "https://json-schema.org/draft/2019-09/vocab/", + allVocabs: map[string]*Schema{ + "core": nil, + "applicator": nil, + "validation": nil, + "meta-data": nil, + "format": nil, + "content": nil, + }, + defaultVocabs: []string{"core", "applicator", "validation"}, + } + + Draft2020 = &Draft{ + version: 2020, + url: "https://json-schema.org/draft/2020-12/schema", + id: "$id", + subschemas: joinSubschemas(Draft2019.subschemas, + schemaPath("prefixItems/[]"), + ), + vocabPrefix: "https://json-schema.org/draft/2020-12/vocab/", + allVocabs: map[string]*Schema{ + "core": nil, + "applicator": nil, + "unevaluated": nil, + "validation": nil, + "meta-data": nil, + "format-annotation": nil, + "format-assertion": nil, + "content": nil, + }, + defaultVocabs: []string{"core", "applicator", "unevaluated", "validation"}, + } + + draftLatest = Draft2020 +) + +func init() { + c := NewCompiler() + c.AssertFormat() + for _, d := range []*Draft{Draft4, Draft6, Draft7, Draft2019, Draft2020} { + d.sch = c.MustCompile(d.url) + for name := range d.allVocabs { + d.allVocabs[name] = c.MustCompile(strings.TrimSuffix(d.url, "schema") + "meta/" + name) + } + } +} + +func draftFromURL(url string) *Draft { + u, frag := split(url) + if frag != "" { + return nil + } + u, ok := strings.CutPrefix(u, "http://") + if !ok { + u, _ = strings.CutPrefix(u, "https://") + } + switch u { + case "json-schema.org/schema": + return draftLatest + case "json-schema.org/draft/2020-12/schema": + return Draft2020 + case "json-schema.org/draft/2019-09/schema": + return Draft2019 + case "json-schema.org/draft-07/schema": + return Draft7 + case "json-schema.org/draft-06/schema": + return Draft6 + case "json-schema.org/draft-04/schema": + return Draft4 + default: + return nil + } +} + +func (d *Draft) getID(obj map[string]any) string { + if d.version < 2019 { + if _, ok := obj["$ref"]; ok { + // All other properties in a "$ref" object MUST be ignored + return "" + } + } + + id, ok := strVal(obj, d.id) + if !ok { + return "" + } + id, _ = split(id) // ignore fragment + return id +} + +func (d *Draft) getVocabs(url url, doc any, vocabularies map[string]*Vocabulary) ([]string, error) { + if d.version < 2019 { + return nil, nil + } + obj, ok := doc.(map[string]any) + if !ok { + return nil, nil + } + v, ok := obj["$vocabulary"] + if !ok { + return nil, nil + } + obj, ok = v.(map[string]any) + if !ok { + return nil, nil + } + + var vocabs []string + for vocab, reqd := range obj { + if reqd, ok := reqd.(bool); !ok || !reqd { + continue + } + name, ok := strings.CutPrefix(vocab, d.vocabPrefix) + if ok { + if _, ok := d.allVocabs[name]; ok { + if !slices.Contains(vocabs, name) { + vocabs = append(vocabs, name) + continue + } + } + } + if _, ok := vocabularies[vocab]; !ok { + return nil, &UnsupportedVocabularyError{url.String(), vocab} + } + if !slices.Contains(vocabs, vocab) { + vocabs = append(vocabs, vocab) + } + } + if !slices.Contains(vocabs, "core") { + vocabs = append(vocabs, "core") + } + return vocabs, nil +} + +// -- + +type dialect struct { + draft *Draft + vocabs []string // nil means use draft.defaultVocabs +} + +func (d *dialect) hasVocab(name string) bool { + if name == "core" || d.draft.version < 2019 { + return true + } + if d.vocabs != nil { + return slices.Contains(d.vocabs, name) + } + return slices.Contains(d.draft.defaultVocabs, name) +} + +func (d *dialect) activeVocabs(assertVocabs bool, vocabularies map[string]*Vocabulary) []string { + if len(vocabularies) == 0 { + return d.vocabs + } + if d.draft.version < 2019 { + assertVocabs = true + } + if !assertVocabs { + return d.vocabs + } + var vocabs []string + if d.vocabs == nil { + vocabs = slices.Clone(d.draft.defaultVocabs) + } else { + vocabs = slices.Clone(d.vocabs) + } + for vocab := range vocabularies { + if !slices.Contains(vocabs, vocab) { + vocabs = append(vocabs, vocab) + } + } + return vocabs +} + +func (d *dialect) getSchema(assertVocabs bool, vocabularies map[string]*Vocabulary) *Schema { + vocabs := d.activeVocabs(assertVocabs, vocabularies) + if vocabs == nil { + return d.draft.sch + } + + var allOf []*Schema + for _, vocab := range vocabs { + sch := d.draft.allVocabs[vocab] + if sch == nil { + if v, ok := vocabularies[vocab]; ok { + sch = v.Schema + } + } + if sch != nil { + allOf = append(allOf, sch) + } + } + if !slices.Contains(vocabs, "core") { + sch := d.draft.allVocabs["core"] + if sch == nil { + sch = d.draft.sch + } + allOf = append(allOf, sch) + } + sch := &Schema{ + Location: "urn:mem:metaschema", + up: urlPtr{url("urn:mem:metaschema"), ""}, + DraftVersion: d.draft.version, + AllOf: allOf, + } + sch.resource = sch + if sch.DraftVersion >= 2020 { + sch.DynamicAnchor = "meta" + sch.dynamicAnchors = map[string]*Schema{ + "meta": sch, + } + } + return sch +} + +// -- + +type ParseIDError struct { + URL string +} + +func (e *ParseIDError) Error() string { + return fmt.Sprintf("error in parsing id at %q", e.URL) +} + +// -- + +type ParseAnchorError struct { + URL string +} + +func (e *ParseAnchorError) Error() string { + return fmt.Sprintf("error in parsing anchor at %q", e.URL) +} + +// -- + +type DuplicateIDError struct { + ID string + URL string + Ptr1 string + Ptr2 string +} + +func (e *DuplicateIDError) Error() string { + return fmt.Sprintf("duplicate id %q in %q at %q and %q", e.ID, e.URL, e.Ptr1, e.Ptr2) +} + +// -- + +type DuplicateAnchorError struct { + Anchor string + URL string + Ptr1 string + Ptr2 string +} + +func (e *DuplicateAnchorError) Error() string { + return fmt.Sprintf("duplicate anchor %q in %q at %q and %q", e.Anchor, e.URL, e.Ptr1, e.Ptr2) +} + +// -- + +func joinSubschemas(a1 []SchemaPath, a2 ...SchemaPath) []SchemaPath { + var a []SchemaPath + a = append(a, a1...) + a = append(a, a2...) + return a +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/format.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/format.go new file mode 100644 index 000000000..b78b22e2a --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/format.go @@ -0,0 +1,708 @@ +package jsonschema + +import ( + "net/netip" + gourl "net/url" + "strconv" + "strings" + "time" +) + +// Format defined specific format. +type Format struct { + // Name of format. + Name string + + // Validate checks if given value is of this format. + Validate func(v any) error +} + +var formats = map[string]*Format{ + "json-pointer": {"json-pointer", validateJSONPointer}, + "relative-json-pointer": {"relative-json-pointer", validateRelativeJSONPointer}, + "uuid": {"uuid", validateUUID}, + "duration": {"duration", validateDuration}, + "period": {"period", validatePeriod}, + "ipv4": {"ipv4", validateIPV4}, + "ipv6": {"ipv6", validateIPV6}, + "hostname": {"hostname", validateHostname}, + "email": {"email", validateEmail}, + "date": {"date", validateDate}, + "time": {"time", validateTime}, + "date-time": {"date-time", validateDateTime}, + "uri": {"uri", validateURI}, + "iri": {"iri", validateURI}, + "uri-reference": {"uri-reference", validateURIReference}, + "iri-reference": {"iri-reference", validateURIReference}, + "uri-template": {"uri-template", validateURITemplate}, + "semver": {"semver", validateSemver}, +} + +// see https://www.rfc-editor.org/rfc/rfc6901#section-3 +func validateJSONPointer(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + if s == "" { + return nil + } + if !strings.HasPrefix(s, "/") { + return LocalizableError("not starting with /") + } + for _, tok := range strings.Split(s, "/")[1:] { + escape := false + for _, ch := range tok { + if escape { + escape = false + if ch != '0' && ch != '1' { + return LocalizableError("~ must be followed by 0 or 1") + } + continue + } + if ch == '~' { + escape = true + continue + } + switch { + case ch >= '\x00' && ch <= '\x2E': + case ch >= '\x30' && ch <= '\x7D': + case ch >= '\x7F' && ch <= '\U0010FFFF': + default: + return LocalizableError("invalid character %q", ch) + } + } + if escape { + return LocalizableError("~ must be followed by 0 or 1") + } + } + return nil +} + +// see https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3 +func validateRelativeJSONPointer(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + + // start with non-negative-integer + numDigits := 0 + for _, ch := range s { + if ch >= '0' && ch <= '9' { + numDigits++ + } else { + break + } + } + if numDigits == 0 { + return LocalizableError("must start with non-negative integer") + } + if numDigits > 1 && strings.HasPrefix(s, "0") { + return LocalizableError("starts with zero") + } + s = s[numDigits:] + + // followed by either json-pointer or '#' + if s == "#" { + return nil + } + return validateJSONPointer(s) +} + +// see https://datatracker.ietf.org/doc/html/rfc4122#page-4 +func validateUUID(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + + hexGroups := []int{8, 4, 4, 4, 12} + groups := strings.Split(s, "-") + if len(groups) != len(hexGroups) { + return LocalizableError("must have %d elements", len(hexGroups)) + } + for i, group := range groups { + if len(group) != hexGroups[i] { + return LocalizableError("element %d must be %d characters long", i+1, hexGroups[i]) + } + for _, ch := range group { + switch { + case ch >= '0' && ch <= '9': + case ch >= 'a' && ch <= 'f': + case ch >= 'A' && ch <= 'F': + default: + return LocalizableError("non-hex character %q", ch) + } + } + } + return nil +} + +// see https://datatracker.ietf.org/doc/html/rfc3339#appendix-A +func validateDuration(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + + // must start with 'P' + s, ok = strings.CutPrefix(s, "P") + if !ok { + return LocalizableError("must start with P") + } + if s == "" { + return LocalizableError("nothing after P") + } + + // dur-week + if s, ok := strings.CutSuffix(s, "W"); ok { + if s == "" { + return LocalizableError("no number in week") + } + for _, ch := range s { + if ch < '0' || ch > '9' { + return LocalizableError("invalid week") + } + } + return nil + } + + allUnits := []string{"YMD", "HMS"} + for i, s := range strings.Split(s, "T") { + if i != 0 && s == "" { + return LocalizableError("no time elements") + } + if i >= len(allUnits) { + return LocalizableError("more than one T") + } + units := allUnits[i] + for s != "" { + digitCount := 0 + for _, ch := range s { + if ch >= '0' && ch <= '9' { + digitCount++ + } else { + break + } + } + if digitCount == 0 { + return LocalizableError("missing number") + } + s = s[digitCount:] + if s == "" { + return LocalizableError("missing unit") + } + unit := s[0] + j := strings.IndexByte(units, unit) + if j == -1 { + if strings.IndexByte(allUnits[i], unit) != -1 { + return LocalizableError("unit %q out of order", unit) + } + return LocalizableError("invalid unit %q", unit) + } + units = units[j+1:] + s = s[1:] + } + } + + return nil +} + +func validateIPV4(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + groups := strings.Split(s, ".") + if len(groups) != 4 { + return LocalizableError("expected four decimals") + } + for _, group := range groups { + if len(group) > 1 && group[0] == '0' { + return LocalizableError("leading zeros") + } + n, err := strconv.Atoi(group) + if err != nil { + return err + } + if n < 0 || n > 255 { + return LocalizableError("decimal must be between 0 and 255") + } + } + return nil +} + +func validateIPV6(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + if !strings.Contains(s, ":") { + return LocalizableError("missing colon") + } + addr, err := netip.ParseAddr(s) + if err != nil { + return err + } + if addr.Zone() != "" { + return LocalizableError("zone id is not a part of ipv6 address") + } + return nil +} + +// see https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names +func validateHostname(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + + // entire hostname (including the delimiting dots but not a trailing dot) has a maximum of 253 ASCII characters + s = strings.TrimSuffix(s, ".") + if len(s) > 253 { + return LocalizableError("more than 253 characters long") + } + + // Hostnames are composed of series of labels concatenated with dots, as are all domain names + for _, label := range strings.Split(s, ".") { + // Each label must be from 1 to 63 characters long + if len(label) < 1 || len(label) > 63 { + return LocalizableError("label must be 1 to 63 characters long") + } + + // labels must not start or end with a hyphen + if strings.HasPrefix(label, "-") { + return LocalizableError("label starts with hyphen") + } + if strings.HasSuffix(label, "-") { + return LocalizableError("label ends with hyphen") + } + + // labels may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner), + // the digits '0' through '9', and the hyphen ('-') + for _, ch := range label { + switch { + case ch >= 'a' && ch <= 'z': + case ch >= 'A' && ch <= 'Z': + case ch >= '0' && ch <= '9': + case ch == '-': + default: + return LocalizableError("invalid character %q", ch) + } + } + } + return nil +} + +// see https://en.wikipedia.org/wiki/Email_address +func validateEmail(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + // entire email address to be no more than 254 characters long + if len(s) > 254 { + return LocalizableError("more than 255 characters long") + } + + // email address is generally recognized as having two parts joined with an at-sign + at := strings.LastIndexByte(s, '@') + if at == -1 { + return LocalizableError("missing @") + } + local, domain := s[:at], s[at+1:] + + // local part may be up to 64 characters long + if len(local) > 64 { + return LocalizableError("local part more than 64 characters long") + } + + if len(local) > 1 && strings.HasPrefix(local, `"`) && strings.HasPrefix(local, `"`) { + // quoted + local := local[1 : len(local)-1] + if strings.IndexByte(local, '\\') != -1 || strings.IndexByte(local, '"') != -1 { + return LocalizableError("backslash and quote are not allowed within quoted local part") + } + } else { + // unquoted + if strings.HasPrefix(local, ".") { + return LocalizableError("starts with dot") + } + if strings.HasSuffix(local, ".") { + return LocalizableError("ends with dot") + } + + // consecutive dots not allowed + if strings.Contains(local, "..") { + return LocalizableError("consecutive dots") + } + + // check allowed chars + for _, ch := range local { + switch { + case ch >= 'a' && ch <= 'z': + case ch >= 'A' && ch <= 'Z': + case ch >= '0' && ch <= '9': + case strings.ContainsRune(".!#$%&'*+-/=?^_`{|}~", ch): + default: + return LocalizableError("invalid character %q", ch) + } + } + } + + // domain if enclosed in brackets, must match an IP address + if strings.HasPrefix(domain, "[") && strings.HasSuffix(domain, "]") { + domain = domain[1 : len(domain)-1] + if rem, ok := strings.CutPrefix(domain, "IPv6:"); ok { + if err := validateIPV6(rem); err != nil { + return LocalizableError("invalid ipv6 address: %v", err) + } + return nil + } + if err := validateIPV4(domain); err != nil { + return LocalizableError("invalid ipv4 address: %v", err) + } + return nil + } + + // domain must match the requirements for a hostname + if err := validateHostname(domain); err != nil { + return LocalizableError("invalid domain: %v", err) + } + + return nil +} + +// see see https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 +func validateDate(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + _, err := time.Parse("2006-01-02", s) + return err +} + +// see https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 +// NOTE: golang time package does not support leap seconds. +func validateTime(v any) error { + str, ok := v.(string) + if !ok { + return nil + } + + // min: hh:mm:ssZ + if len(str) < 9 { + return LocalizableError("less than 9 characters long") + } + if str[2] != ':' || str[5] != ':' { + return LocalizableError("missing colon in correct place") + } + + // parse hh:mm:ss + var hms []int + for _, tok := range strings.SplitN(str[:8], ":", 3) { + i, err := strconv.Atoi(tok) + if err != nil { + return LocalizableError("invalid hour/min/sec") + } + if i < 0 { + return LocalizableError("non-positive hour/min/sec") + } + hms = append(hms, i) + } + if len(hms) != 3 { + return LocalizableError("missing hour/min/sec") + } + h, m, s := hms[0], hms[1], hms[2] + if h > 23 || m > 59 || s > 60 { + return LocalizableError("hour/min/sec out of range") + } + str = str[8:] + + // parse sec-frac if present + if rem, ok := strings.CutPrefix(str, "."); ok { + numDigits := 0 + for _, ch := range rem { + if ch >= '0' && ch <= '9' { + numDigits++ + } else { + break + } + } + if numDigits == 0 { + return LocalizableError("no digits in second fraction") + } + str = rem[numDigits:] + } + + if str != "z" && str != "Z" { + // parse time-numoffset + if len(str) != 6 { + return LocalizableError("offset must be 6 characters long") + } + var sign int + switch str[0] { + case '+': + sign = -1 + case '-': + sign = +1 + default: + return LocalizableError("offset must begin with plus/minus") + } + str = str[1:] + if str[2] != ':' { + return LocalizableError("missing colon in offset in correct place") + } + + var zhm []int + for _, tok := range strings.SplitN(str, ":", 2) { + i, err := strconv.Atoi(tok) + if err != nil { + return LocalizableError("invalid hour/min in offset") + } + if i < 0 { + return LocalizableError("non-positive hour/min in offset") + } + zhm = append(zhm, i) + } + zh, zm := zhm[0], zhm[1] + if zh > 23 || zm > 59 { + return LocalizableError("hour/min in offset out of range") + } + + // apply timezone + hm := (h*60 + m) + sign*(zh*60+zm) + if hm < 0 { + hm += 24 * 60 + } + h, m = hm/60, hm%60 + } + + // check leap second + if s >= 60 && (h != 23 || m != 59) { + return LocalizableError("invalid leap second") + } + + return nil +} + +// see https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 +func validateDateTime(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + + // min: yyyy-mm-ddThh:mm:ssZ + if len(s) < 20 { + return LocalizableError("less than 20 characters long") + } + + if s[10] != 't' && s[10] != 'T' { + return LocalizableError("11th character must be t or T") + } + if err := validateDate(s[:10]); err != nil { + return LocalizableError("invalid date element: %v", err) + } + if err := validateTime(s[11:]); err != nil { + return LocalizableError("invalid time element: %v", err) + } + return nil +} + +func parseURL(s string) (*gourl.URL, error) { + u, err := gourl.Parse(s) + if err != nil { + return nil, err + } + + // gourl does not validate ipv6 host address + hostName := u.Hostname() + if strings.Contains(hostName, ":") { + if !strings.Contains(u.Host, "[") || !strings.Contains(u.Host, "]") { + return nil, LocalizableError("ipv6 address not enclosed in brackets") + } + if err := validateIPV6(hostName); err != nil { + return nil, LocalizableError("invalid ipv6 address: %v", err) + } + } + + return u, nil +} + +func validateURI(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + u, err := parseURL(s) + if err != nil { + return err + } + if !u.IsAbs() { + return LocalizableError("relative url") + } + return nil +} + +func validateURIReference(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + if strings.Contains(s, `\`) { + return LocalizableError(`contains \`) + } + _, err := parseURL(s) + return err +} + +func validateURITemplate(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + u, err := parseURL(s) + if err != nil { + return err + } + for _, tok := range strings.Split(u.RawPath, "/") { + tok, err = decode(tok) + if err != nil { + return LocalizableError("percent decode failed: %v", err) + } + want := true + for _, ch := range tok { + var got bool + switch ch { + case '{': + got = true + case '}': + got = false + default: + continue + } + if got != want { + return LocalizableError("nested curly braces") + } + want = !want + } + if !want { + return LocalizableError("no matching closing brace") + } + } + return nil +} + +func validatePeriod(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + + slash := strings.IndexByte(s, '/') + if slash == -1 { + return LocalizableError("missing slash") + } + + start, end := s[:slash], s[slash+1:] + if strings.HasPrefix(start, "P") { + if err := validateDuration(start); err != nil { + return LocalizableError("invalid start duration: %v", err) + } + if err := validateDateTime(end); err != nil { + return LocalizableError("invalid end date-time: %v", err) + } + } else { + if err := validateDateTime(start); err != nil { + return LocalizableError("invalid start date-time: %v", err) + } + if strings.HasPrefix(end, "P") { + if err := validateDuration(end); err != nil { + return LocalizableError("invalid end duration: %v", err) + } + } else if err := validateDateTime(end); err != nil { + return LocalizableError("invalid end date-time: %v", err) + } + } + + return nil +} + +// see https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions +func validateSemver(v any) error { + s, ok := v.(string) + if !ok { + return nil + } + + // build -- + if i := strings.IndexByte(s, '+'); i != -1 { + build := s[i+1:] + if build == "" { + return LocalizableError("build is empty") + } + for _, buildID := range strings.Split(build, ".") { + if buildID == "" { + return LocalizableError("build identifier is empty") + } + for _, ch := range buildID { + switch { + case ch >= '0' && ch <= '9': + case (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '-': + default: + return LocalizableError("invalid character %q in build identifier", ch) + } + } + } + s = s[:i] + } + + // pre-release -- + if i := strings.IndexByte(s, '-'); i != -1 { + preRelease := s[i+1:] + for _, preReleaseID := range strings.Split(preRelease, ".") { + if preReleaseID == "" { + return LocalizableError("pre-release identifier is empty") + } + allDigits := true + for _, ch := range preReleaseID { + switch { + case ch >= '0' && ch <= '9': + case (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '-': + allDigits = false + default: + return LocalizableError("invalid character %q in pre-release identifier", ch) + } + } + if allDigits && len(preReleaseID) > 1 && preReleaseID[0] == '0' { + return LocalizableError("pre-release numeric identifier starts with zero") + } + } + s = s[:i] + } + + // versionCore -- + versions := strings.Split(s, ".") + if len(versions) != 3 { + return LocalizableError("versionCore must have 3 numbers separated by dot") + } + names := []string{"major", "minor", "patch"} + for i, version := range versions { + if version == "" { + return LocalizableError("%s is empty", names[i]) + } + if len(version) > 1 && version[0] == '0' { + return LocalizableError("%s starts with zero", names[i]) + } + for _, ch := range version { + if ch < '0' || ch > '9' { + return LocalizableError("%s contains non-digit", names[i]) + } + } + } + + return nil +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work b/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work new file mode 100644 index 000000000..e7f4d93de --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work @@ -0,0 +1,8 @@ +go 1.21.1 + +use ( + . + ./cmd/jv +) + +// replace github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 => ./ diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work.sum b/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work.sum new file mode 100644 index 000000000..2b5b811d9 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work.sum @@ -0,0 +1,4 @@ +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/kind/kind.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/kind/kind.go new file mode 100644 index 000000000..a37fb0b97 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/kind/kind.go @@ -0,0 +1,651 @@ +package kind + +import ( + "fmt" + "math/big" + "strings" + + "golang.org/x/text/message" +) + +// -- + +type InvalidJsonValue struct { + Value any +} + +func (*InvalidJsonValue) KeywordPath() []string { + return nil +} + +func (k *InvalidJsonValue) LocalizedString(p *message.Printer) string { + return p.Sprintf("invalid jsonType %T", k.Value) +} + +// -- + +type Schema struct { + Location string +} + +func (*Schema) KeywordPath() []string { + return nil +} + +func (k *Schema) LocalizedString(p *message.Printer) string { + return p.Sprintf("jsonschema validation failed with %s", quote(k.Location)) +} + +// -- + +type Group struct{} + +func (*Group) KeywordPath() []string { + return nil +} + +func (*Group) LocalizedString(p *message.Printer) string { + return p.Sprintf("validation failed") +} + +// -- + +type Not struct{} + +func (*Not) KeywordPath() []string { + return nil +} + +func (*Not) LocalizedString(p *message.Printer) string { + return p.Sprintf("'not' failed") +} + +// -- + +type AllOf struct{} + +func (*AllOf) KeywordPath() []string { + return []string{"allOf"} +} + +func (*AllOf) LocalizedString(p *message.Printer) string { + return p.Sprintf("'allOf' failed") +} + +// -- + +type AnyOf struct{} + +func (*AnyOf) KeywordPath() []string { + return []string{"anyOf"} +} + +func (*AnyOf) LocalizedString(p *message.Printer) string { + return p.Sprintf("'anyOf' failed") +} + +// -- + +type OneOf struct { + // Subschemas gives indexes of Subschemas that have matched. + // Value nil, means none of the subschemas matched. + Subschemas []int +} + +func (*OneOf) KeywordPath() []string { + return []string{"oneOf"} +} + +func (k *OneOf) LocalizedString(p *message.Printer) string { + if len(k.Subschemas) == 0 { + return p.Sprintf("'oneOf' failed, none matched") + } + return p.Sprintf("'oneOf' failed, subschemas %d, %d matched", k.Subschemas[0], k.Subschemas[1]) +} + +//-- + +type FalseSchema struct{} + +func (*FalseSchema) KeywordPath() []string { + return nil +} + +func (*FalseSchema) LocalizedString(p *message.Printer) string { + return p.Sprintf("false schema") +} + +// -- + +type RefCycle struct { + URL string + KeywordLocation1 string + KeywordLocation2 string +} + +func (*RefCycle) KeywordPath() []string { + return nil +} + +func (k *RefCycle) LocalizedString(p *message.Printer) string { + return p.Sprintf("both %s and %s resolve to %q causing reference cycle", k.KeywordLocation1, k.KeywordLocation2, k.URL) +} + +// -- + +type Type struct { + Got string + Want []string +} + +func (*Type) KeywordPath() []string { + return []string{"type"} +} + +func (k *Type) LocalizedString(p *message.Printer) string { + want := strings.Join(k.Want, " or ") + return p.Sprintf("got %s, want %s", k.Got, want) +} + +// -- + +type Enum struct { + Got any + Want []any +} + +// KeywordPath implements jsonschema.ErrorKind. +func (*Enum) KeywordPath() []string { + return []string{"enum"} +} + +func (k *Enum) LocalizedString(p *message.Printer) string { + allPrimitive := true +loop: + for _, item := range k.Want { + switch item.(type) { + case []any, map[string]any: + allPrimitive = false + break loop + } + } + if allPrimitive { + if len(k.Want) == 1 { + return p.Sprintf("value must be %s", display(k.Want[0])) + } + var want []string + for _, v := range k.Want { + want = append(want, display(v)) + } + return p.Sprintf("value must be one of %s", strings.Join(want, ", ")) + } + return p.Sprintf("'enum' failed") +} + +// -- + +type Const struct { + Got any + Want any +} + +func (*Const) KeywordPath() []string { + return []string{"const"} +} + +func (k *Const) LocalizedString(p *message.Printer) string { + switch want := k.Want.(type) { + case []any, map[string]any: + return p.Sprintf("'const' failed") + default: + return p.Sprintf("value must be %s", display(want)) + } +} + +// -- + +type Format struct { + Got any + Want string + Err error +} + +func (*Format) KeywordPath() []string { + return []string{"format"} +} + +func (k *Format) LocalizedString(p *message.Printer) string { + return p.Sprintf("%s is not valid %s: %v", display(k.Got), k.Want, localizedError(k.Err, p)) +} + +// -- + +type Reference struct { + Keyword string + URL string +} + +func (k *Reference) KeywordPath() []string { + return []string{k.Keyword} +} + +func (*Reference) LocalizedString(p *message.Printer) string { + return p.Sprintf("validation failed") +} + +// -- + +type MinProperties struct { + Got, Want int +} + +func (*MinProperties) KeywordPath() []string { + return []string{"minProperties"} +} + +func (k *MinProperties) LocalizedString(p *message.Printer) string { + return p.Sprintf("minProperties: got %d, want %d", k.Got, k.Want) +} + +// -- + +type MaxProperties struct { + Got, Want int +} + +func (*MaxProperties) KeywordPath() []string { + return []string{"maxProperties"} +} + +func (k *MaxProperties) LocalizedString(p *message.Printer) string { + return p.Sprintf("maxProperties: got %d, want %d", k.Got, k.Want) +} + +// -- + +type MinItems struct { + Got, Want int +} + +func (*MinItems) KeywordPath() []string { + return []string{"minItems"} +} + +func (k *MinItems) LocalizedString(p *message.Printer) string { + return p.Sprintf("minItems: got %d, want %d", k.Got, k.Want) +} + +// -- + +type MaxItems struct { + Got, Want int +} + +func (*MaxItems) KeywordPath() []string { + return []string{"maxItems"} +} + +func (k *MaxItems) LocalizedString(p *message.Printer) string { + return p.Sprintf("maxItems: got %d, want %d", k.Got, k.Want) +} + +// -- + +type AdditionalItems struct { + Count int +} + +func (*AdditionalItems) KeywordPath() []string { + return []string{"additionalItems"} +} + +func (k *AdditionalItems) LocalizedString(p *message.Printer) string { + return p.Sprintf("last %d additionalItem(s) not allowed", k.Count) +} + +// -- + +type Required struct { + Missing []string +} + +func (*Required) KeywordPath() []string { + return []string{"required"} +} + +func (k *Required) LocalizedString(p *message.Printer) string { + if len(k.Missing) == 1 { + return p.Sprintf("missing property %s", quote(k.Missing[0])) + } + return p.Sprintf("missing properties %s", joinQuoted(k.Missing, ", ")) +} + +// -- + +type Dependency struct { + Prop string // dependency of prop that failed + Missing []string // missing props +} + +func (k *Dependency) KeywordPath() []string { + return []string{"dependency", k.Prop} +} + +func (k *Dependency) LocalizedString(p *message.Printer) string { + return p.Sprintf("properties %s required, if %s exists", joinQuoted(k.Missing, ", "), quote(k.Prop)) +} + +// -- + +type DependentRequired struct { + Prop string // dependency of prop that failed + Missing []string // missing props +} + +func (k *DependentRequired) KeywordPath() []string { + return []string{"dependentRequired", k.Prop} +} + +func (k *DependentRequired) LocalizedString(p *message.Printer) string { + return p.Sprintf("properties %s required, if %s exists", joinQuoted(k.Missing, ", "), quote(k.Prop)) +} + +// -- + +type AdditionalProperties struct { + Properties []string +} + +func (*AdditionalProperties) KeywordPath() []string { + return []string{"additionalProperties"} +} + +func (k *AdditionalProperties) LocalizedString(p *message.Printer) string { + return p.Sprintf("additional properties %s not allowed", joinQuoted(k.Properties, ", ")) +} + +// -- + +type PropertyNames struct { + Property string +} + +func (*PropertyNames) KeywordPath() []string { + return []string{"propertyNames"} +} + +func (k *PropertyNames) LocalizedString(p *message.Printer) string { + return p.Sprintf("invalid propertyName %s", quote(k.Property)) +} + +// -- + +type UniqueItems struct { + Duplicates [2]int +} + +func (*UniqueItems) KeywordPath() []string { + return []string{"uniqueItems"} +} + +func (k *UniqueItems) LocalizedString(p *message.Printer) string { + return p.Sprintf("items at %d and %d are equal", k.Duplicates[0], k.Duplicates[1]) +} + +// -- + +type Contains struct{} + +func (*Contains) KeywordPath() []string { + return []string{"contains"} +} + +func (*Contains) LocalizedString(p *message.Printer) string { + return p.Sprintf("no items match contains schema") +} + +// -- + +type MinContains struct { + Got []int + Want int +} + +func (*MinContains) KeywordPath() []string { + return []string{"minContains"} +} + +func (k *MinContains) LocalizedString(p *message.Printer) string { + if len(k.Got) == 0 { + return p.Sprintf("min %d items required to match contains schema, but none matched", k.Want) + } else { + got := fmt.Sprintf("%v", k.Got) + return p.Sprintf("min %d items required to match contains schema, but matched %d items at %v", k.Want, len(k.Got), got[1:len(got)-1]) + } +} + +// -- + +type MaxContains struct { + Got []int + Want int +} + +func (*MaxContains) KeywordPath() []string { + return []string{"maxContains"} +} + +func (k *MaxContains) LocalizedString(p *message.Printer) string { + got := fmt.Sprintf("%v", k.Got) + return p.Sprintf("max %d items required to match contains schema, but matched %d items at %v", k.Want, len(k.Got), got[1:len(got)-1]) +} + +// -- + +type MinLength struct { + Got, Want int +} + +func (*MinLength) KeywordPath() []string { + return []string{"minLength"} +} + +func (k *MinLength) LocalizedString(p *message.Printer) string { + return p.Sprintf("minLength: got %d, want %d", k.Got, k.Want) +} + +// -- + +type MaxLength struct { + Got, Want int +} + +func (*MaxLength) KeywordPath() []string { + return []string{"maxLength"} +} + +func (k *MaxLength) LocalizedString(p *message.Printer) string { + return p.Sprintf("maxLength: got %d, want %d", k.Got, k.Want) +} + +// -- + +type Pattern struct { + Got string + Want string +} + +func (*Pattern) KeywordPath() []string { + return []string{"pattern"} +} + +func (k *Pattern) LocalizedString(p *message.Printer) string { + return p.Sprintf("%s does not match pattern %s", quote(k.Got), quote(k.Want)) +} + +// -- + +type ContentEncoding struct { + Want string + Err error +} + +func (*ContentEncoding) KeywordPath() []string { + return []string{"contentEncoding"} +} + +func (k *ContentEncoding) LocalizedString(p *message.Printer) string { + return p.Sprintf("value is not %s encoded: %v", quote(k.Want), localizedError(k.Err, p)) +} + +// -- + +type ContentMediaType struct { + Got []byte + Want string + Err error +} + +func (*ContentMediaType) KeywordPath() []string { + return []string{"contentMediaType"} +} + +func (k *ContentMediaType) LocalizedString(p *message.Printer) string { + return p.Sprintf("value if not of mediatype %s: %v", quote(k.Want), k.Err) +} + +// -- + +type ContentSchema struct{} + +func (*ContentSchema) KeywordPath() []string { + return []string{"contentSchema"} +} + +func (*ContentSchema) LocalizedString(p *message.Printer) string { + return p.Sprintf("'contentSchema' failed") +} + +// -- + +type Minimum struct { + Got *big.Rat + Want *big.Rat +} + +func (*Minimum) KeywordPath() []string { + return []string{"minimum"} +} + +func (k *Minimum) LocalizedString(p *message.Printer) string { + got, _ := k.Got.Float64() + want, _ := k.Want.Float64() + return p.Sprintf("minimum: got %v, want %v", got, want) +} + +// -- + +type Maximum struct { + Got *big.Rat + Want *big.Rat +} + +func (*Maximum) KeywordPath() []string { + return []string{"maximum"} +} + +func (k *Maximum) LocalizedString(p *message.Printer) string { + got, _ := k.Got.Float64() + want, _ := k.Want.Float64() + return p.Sprintf("maximum: got %v, want %v", got, want) +} + +// -- + +type ExclusiveMinimum struct { + Got *big.Rat + Want *big.Rat +} + +func (*ExclusiveMinimum) KeywordPath() []string { + return []string{"exclusiveMinimum"} +} + +func (k *ExclusiveMinimum) LocalizedString(p *message.Printer) string { + got, _ := k.Got.Float64() + want, _ := k.Want.Float64() + return p.Sprintf("exclusiveMinimum: got %v, want %v", got, want) +} + +// -- + +type ExclusiveMaximum struct { + Got *big.Rat + Want *big.Rat +} + +func (*ExclusiveMaximum) KeywordPath() []string { + return []string{"exclusiveMaximum"} +} + +func (k *ExclusiveMaximum) LocalizedString(p *message.Printer) string { + got, _ := k.Got.Float64() + want, _ := k.Want.Float64() + return p.Sprintf("exclusiveMaximum: got %v, want %v", got, want) +} + +// -- + +type MultipleOf struct { + Got *big.Rat + Want *big.Rat +} + +func (*MultipleOf) KeywordPath() []string { + return []string{"multipleOf"} +} + +func (k *MultipleOf) LocalizedString(p *message.Printer) string { + got, _ := k.Got.Float64() + want, _ := k.Want.Float64() + return p.Sprintf("multipleOf: got %v, want %v", got, want) +} + +// -- + +func quote(s string) string { + s = fmt.Sprintf("%q", s) + s = strings.ReplaceAll(s, `\"`, `"`) + s = strings.ReplaceAll(s, `'`, `\'`) + return "'" + s[1:len(s)-1] + "'" +} + +func joinQuoted(arr []string, sep string) string { + var sb strings.Builder + for _, s := range arr { + if sb.Len() > 0 { + sb.WriteString(sep) + } + sb.WriteString(quote(s)) + } + return sb.String() +} + +// to be used only for primitive. +func display(v any) string { + switch v := v.(type) { + case string: + return quote(v) + case []any, map[string]any: + return "value" + default: + return fmt.Sprintf("%v", v) + } +} + +func localizedError(err error, p *message.Printer) string { + if err, ok := err.(interface{ LocalizedError(*message.Printer) string }); ok { + return err.LocalizedError(p) + } + return err.Error() +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/loader.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/loader.go new file mode 100644 index 000000000..ce0170e20 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/loader.go @@ -0,0 +1,266 @@ +package jsonschema + +import ( + "embed" + "encoding/json" + "errors" + "fmt" + "io" + "io/fs" + gourl "net/url" + "os" + "path/filepath" + "runtime" + "strings" +) + +// URLLoader knows how to load json from given url. +type URLLoader interface { + // Load loads json from given absolute url. + Load(url string) (any, error) +} + +// -- + +// FileLoader loads json file url. +type FileLoader struct{} + +func (l FileLoader) Load(url string) (any, error) { + path, err := l.ToFile(url) + if err != nil { + return nil, err + } + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + return UnmarshalJSON(f) +} + +// ToFile is helper method to convert file url to file path. +func (l FileLoader) ToFile(url string) (string, error) { + u, err := gourl.Parse(url) + if err != nil { + return "", err + } + if u.Scheme != "file" { + return "", fmt.Errorf("invalid file url: %s", u) + } + path := u.Path + if runtime.GOOS == "windows" { + path = strings.TrimPrefix(path, "/") + path = filepath.FromSlash(path) + } + return path, nil +} + +// -- + +// SchemeURLLoader delegates to other [URLLoaders] +// based on url scheme. +type SchemeURLLoader map[string]URLLoader + +func (l SchemeURLLoader) Load(url string) (any, error) { + u, err := gourl.Parse(url) + if err != nil { + return nil, err + } + ll, ok := l[u.Scheme] + if !ok { + return nil, &UnsupportedURLSchemeError{u.String()} + } + return ll.Load(url) +} + +// -- + +//go:embed metaschemas +var metaFS embed.FS + +func openMeta(url string) (fs.File, error) { + u, meta := strings.CutPrefix(url, "http://json-schema.org/") + if !meta { + u, meta = strings.CutPrefix(url, "https://json-schema.org/") + } + if meta { + if u == "schema" { + return openMeta(draftLatest.url) + } + f, err := metaFS.Open("metaschemas/" + u) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return nil, nil + } + return nil, err + } + return f, err + } + return nil, nil + +} + +func isMeta(url string) bool { + f, err := openMeta(url) + if err != nil { + return true + } + if f != nil { + f.Close() + return true + } + return false +} + +func loadMeta(url string) (any, error) { + f, err := openMeta(url) + if err != nil { + return nil, err + } + if f == nil { + return nil, nil + } + defer f.Close() + return UnmarshalJSON(f) +} + +// -- + +type defaultLoader struct { + docs map[url]any // docs loaded so far + loader URLLoader +} + +func (l *defaultLoader) add(url url, doc any) bool { + if _, ok := l.docs[url]; ok { + return false + } + l.docs[url] = doc + return true +} + +func (l *defaultLoader) load(url url) (any, error) { + if doc, ok := l.docs[url]; ok { + return doc, nil + } + doc, err := loadMeta(url.String()) + if err != nil { + return nil, err + } + if doc != nil { + l.add(url, doc) + return doc, nil + } + if l.loader == nil { + return nil, &LoadURLError{url.String(), errors.New("no URLLoader set")} + } + doc, err = l.loader.Load(url.String()) + if err != nil { + return nil, &LoadURLError{URL: url.String(), Err: err} + } + l.add(url, doc) + return doc, nil +} + +func (l *defaultLoader) getDraft(up urlPtr, doc any, defaultDraft *Draft, cycle map[url]struct{}) (*Draft, error) { + obj, ok := doc.(map[string]any) + if !ok { + return defaultDraft, nil + } + sch, ok := strVal(obj, "$schema") + if !ok { + return defaultDraft, nil + } + if draft := draftFromURL(sch); draft != nil { + return draft, nil + } + sch, _ = split(sch) + if _, err := gourl.Parse(sch); err != nil { + return nil, &InvalidMetaSchemaURLError{up.String(), err} + } + schUrl := url(sch) + if up.ptr.isEmpty() && schUrl == up.url { + return nil, &UnsupportedDraftError{schUrl.String()} + } + if _, ok := cycle[schUrl]; ok { + return nil, &MetaSchemaCycleError{schUrl.String()} + } + cycle[schUrl] = struct{}{} + doc, err := l.load(schUrl) + if err != nil { + return nil, err + } + return l.getDraft(urlPtr{schUrl, ""}, doc, defaultDraft, cycle) +} + +func (l *defaultLoader) getMetaVocabs(doc any, draft *Draft, vocabularies map[string]*Vocabulary) ([]string, error) { + obj, ok := doc.(map[string]any) + if !ok { + return nil, nil + } + sch, ok := strVal(obj, "$schema") + if !ok { + return nil, nil + } + if draft := draftFromURL(sch); draft != nil { + return nil, nil + } + sch, _ = split(sch) + if _, err := gourl.Parse(sch); err != nil { + return nil, &ParseURLError{sch, err} + } + schUrl := url(sch) + doc, err := l.load(schUrl) + if err != nil { + return nil, err + } + return draft.getVocabs(schUrl, doc, vocabularies) +} + +// -- + +type LoadURLError struct { + URL string + Err error +} + +func (e *LoadURLError) Error() string { + return fmt.Sprintf("failing loading %q: %v", e.URL, e.Err) +} + +// -- + +type UnsupportedURLSchemeError struct { + url string +} + +func (e *UnsupportedURLSchemeError) Error() string { + return fmt.Sprintf("no URLLoader registered for %q", e.url) +} + +// -- + +type ResourceExistsError struct { + url string +} + +func (e *ResourceExistsError) Error() string { + return fmt.Sprintf("resource for %q already exists", e.url) +} + +// -- + +// UnmarshalJSON unmarshals into [any] without losing +// number precision using [json.Number]. +func UnmarshalJSON(r io.Reader) (any, error) { + decoder := json.NewDecoder(r) + decoder.UseNumber() + var doc any + if err := decoder.Decode(&doc); err != nil { + return nil, err + } + if _, err := decoder.Token(); err == nil || err != io.EOF { + return nil, fmt.Errorf("invalid character after top-level value") + } + return doc, nil +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft-04/schema b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft-04/schema new file mode 100644 index 000000000..b2a7ff0f5 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft-04/schema @@ -0,0 +1,151 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "positiveInteger": { + "type": "integer", + "minimum": 0 + }, + "positiveIntegerDefault0": { + "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] + }, + "simpleTypes": { + "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "uniqueItems": true + } + }, + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uriref" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "multipleOf": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "boolean", + "default": false + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "boolean", + "default": false + }, + "maxLength": { "$ref": "#/definitions/positiveInteger" }, + "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/positiveInteger" }, + "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxProperties": { "$ref": "#/definitions/positiveInteger" }, + "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" }, + "format": { "type": "string" }, + "$ref": { "type": "string" } + }, + "dependencies": { + "exclusiveMaximum": [ "maximum" ], + "exclusiveMinimum": [ "minimum" ] + }, + "default": {} +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft-06/schema b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft-06/schema new file mode 100644 index 000000000..fa22ad1b0 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft-06/schema @@ -0,0 +1,150 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "http://json-schema.org/draft-06/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": {}, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": {} +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft-07/schema b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft-07/schema new file mode 100644 index 000000000..326759a62 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft-07/schema @@ -0,0 +1,172 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": true + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "if": { "$ref": "#" }, + "then": { "$ref": "#" }, + "else": { "$ref": "#" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": true +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/applicator b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/applicator new file mode 100644 index 000000000..857d2d495 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/applicator @@ -0,0 +1,55 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/applicator", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/applicator": true + }, + "$recursiveAnchor": true, + "title": "Applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "additionalItems": { "$recursiveRef": "#" }, + "unevaluatedItems": { "$recursiveRef": "#" }, + "items": { + "anyOf": [ + { "$recursiveRef": "#" }, + { "$ref": "#/$defs/schemaArray" } + ] + }, + "contains": { "$recursiveRef": "#" }, + "additionalProperties": { "$recursiveRef": "#" }, + "unevaluatedProperties": { "$recursiveRef": "#" }, + "properties": { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependentSchemas": { + "type": "object", + "additionalProperties": { + "$recursiveRef": "#" + } + }, + "propertyNames": { "$recursiveRef": "#" }, + "if": { "$recursiveRef": "#" }, + "then": { "$recursiveRef": "#" }, + "else": { "$recursiveRef": "#" }, + "allOf": { "$ref": "#/$defs/schemaArray" }, + "anyOf": { "$ref": "#/$defs/schemaArray" }, + "oneOf": { "$ref": "#/$defs/schemaArray" }, + "not": { "$recursiveRef": "#" } + }, + "$defs": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$recursiveRef": "#" } + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/content b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/content new file mode 100644 index 000000000..fa5d20b8d --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/content @@ -0,0 +1,15 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/content", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/content": true + }, + "$recursiveAnchor": true, + "title": "Content vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "contentSchema": { "$recursiveRef": "#" } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/core b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/core new file mode 100644 index 000000000..bf5731985 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/core @@ -0,0 +1,56 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/core", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/core": true + }, + "$recursiveAnchor": true, + "title": "Core vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference", + "$comment": "Non-empty fragments not allowed.", + "pattern": "^[^#]*#?$" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$anchor": { + "type": "string", + "pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveRef": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveAnchor": { + "type": "boolean", + "default": false + }, + "$vocabulary": { + "type": "object", + "propertyNames": { + "type": "string", + "format": "uri" + }, + "additionalProperties": { + "type": "boolean" + } + }, + "$comment": { + "type": "string" + }, + "$defs": { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" }, + "default": {} + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/format b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/format new file mode 100644 index 000000000..fe553c239 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/format @@ -0,0 +1,13 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/format", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/format": true + }, + "$recursiveAnchor": true, + "title": "Format vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "format": { "type": "string" } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/meta-data b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/meta-data new file mode 100644 index 000000000..5c95715c4 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/meta-data @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/meta-data", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/meta-data": true + }, + "$recursiveAnchor": true, + "title": "Meta-data vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "deprecated": { + "type": "boolean", + "default": false + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/validation b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/validation new file mode 100644 index 000000000..f3525e076 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/meta/validation @@ -0,0 +1,97 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/validation", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/validation": true + }, + "$recursiveAnchor": true, + "title": "Validation vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/$defs/nonNegativeInteger" }, + "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": { "$ref": "#/$defs/nonNegativeInteger" }, + "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxContains": { "$ref": "#/$defs/nonNegativeInteger" }, + "minContains": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 1 + }, + "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" }, + "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/$defs/stringArray" }, + "dependentRequired": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/stringArray" + } + }, + "const": true, + "enum": { + "type": "array", + "items": true + }, + "type": { + "anyOf": [ + { "$ref": "#/$defs/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/$defs/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + } + }, + "$defs": { + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 0 + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/schema b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/schema new file mode 100644 index 000000000..f433389be --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2019-09/schema @@ -0,0 +1,41 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/schema", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/core": true, + "https://json-schema.org/draft/2019-09/vocab/applicator": true, + "https://json-schema.org/draft/2019-09/vocab/validation": true, + "https://json-schema.org/draft/2019-09/vocab/meta-data": true, + "https://json-schema.org/draft/2019-09/vocab/format": false, + "https://json-schema.org/draft/2019-09/vocab/content": true + }, + "$recursiveAnchor": true, + "title": "Core and Validation specifications meta-schema", + "allOf": [ + {"$ref": "meta/core"}, + {"$ref": "meta/applicator"}, + {"$ref": "meta/validation"}, + {"$ref": "meta/meta-data"}, + {"$ref": "meta/format"}, + {"$ref": "meta/content"} + ], + "type": ["object", "boolean"], + "properties": { + "definitions": { + "$comment": "While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.", + "type": "object", + "additionalProperties": { "$recursiveRef": "#" }, + "default": {} + }, + "dependencies": { + "$comment": "\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"", + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$recursiveRef": "#" }, + { "$ref": "meta/validation#/$defs/stringArray" } + ] + } + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/applicator b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/applicator new file mode 100644 index 000000000..0ef24edc8 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/applicator @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/applicator", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/applicator": true + }, + "$dynamicAnchor": "meta", + "title": "Applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "prefixItems": { "$ref": "#/$defs/schemaArray" }, + "items": { "$dynamicRef": "#meta" }, + "contains": { "$dynamicRef": "#meta" }, + "additionalProperties": { "$dynamicRef": "#meta" }, + "properties": { + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependentSchemas": { + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" }, + "default": {} + }, + "propertyNames": { "$dynamicRef": "#meta" }, + "if": { "$dynamicRef": "#meta" }, + "then": { "$dynamicRef": "#meta" }, + "else": { "$dynamicRef": "#meta" }, + "allOf": { "$ref": "#/$defs/schemaArray" }, + "anyOf": { "$ref": "#/$defs/schemaArray" }, + "oneOf": { "$ref": "#/$defs/schemaArray" }, + "not": { "$dynamicRef": "#meta" } + }, + "$defs": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$dynamicRef": "#meta" } + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/content b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/content new file mode 100644 index 000000000..0330ff0a8 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/content @@ -0,0 +1,15 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/content", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/content": true + }, + "$dynamicAnchor": "meta", + "title": "Content vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "contentEncoding": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentSchema": { "$dynamicRef": "#meta" } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/core b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/core new file mode 100644 index 000000000..c4de7005a --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/core @@ -0,0 +1,50 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/core", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true + }, + "$dynamicAnchor": "meta", + "title": "Core vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "$id": { + "$ref": "#/$defs/uriReferenceString", + "$comment": "Non-empty fragments not allowed.", + "pattern": "^[^#]*#?$" + }, + "$schema": { "$ref": "#/$defs/uriString" }, + "$ref": { "$ref": "#/$defs/uriReferenceString" }, + "$anchor": { "$ref": "#/$defs/anchorString" }, + "$dynamicRef": { "$ref": "#/$defs/uriReferenceString" }, + "$dynamicAnchor": { "$ref": "#/$defs/anchorString" }, + "$vocabulary": { + "type": "object", + "propertyNames": { "$ref": "#/$defs/uriString" }, + "additionalProperties": { + "type": "boolean" + } + }, + "$comment": { + "type": "string" + }, + "$defs": { + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" } + } + }, + "$defs": { + "anchorString": { + "type": "string", + "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$" + }, + "uriString": { + "type": "string", + "format": "uri" + }, + "uriReferenceString": { + "type": "string", + "format": "uri-reference" + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/format-annotation b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/format-annotation new file mode 100644 index 000000000..0aa07d1c1 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/format-annotation @@ -0,0 +1,13 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/format-annotation", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true + }, + "$dynamicAnchor": "meta", + "title": "Format vocabulary meta-schema for annotation results", + "type": ["object", "boolean"], + "properties": { + "format": { "type": "string" } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/format-assertion b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/format-assertion new file mode 100644 index 000000000..38613bff6 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/format-assertion @@ -0,0 +1,13 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/format-assertion", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/format-assertion": true + }, + "$dynamicAnchor": "meta", + "title": "Format vocabulary meta-schema for assertion results", + "type": ["object", "boolean"], + "properties": { + "format": { "type": "string" } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/meta-data b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/meta-data new file mode 100644 index 000000000..30e283714 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/meta-data @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/meta-data", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/meta-data": true + }, + "$dynamicAnchor": "meta", + "title": "Meta-data vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "deprecated": { + "type": "boolean", + "default": false + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/unevaluated b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/unevaluated new file mode 100644 index 000000000..e9e093d12 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/unevaluated @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/unevaluated", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/unevaluated": true + }, + "$dynamicAnchor": "meta", + "title": "Unevaluated applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "unevaluatedItems": { "$dynamicRef": "#meta" }, + "unevaluatedProperties": { "$dynamicRef": "#meta" } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/validation b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/validation new file mode 100644 index 000000000..4e016ed2b --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/meta/validation @@ -0,0 +1,97 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/validation", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/validation": true + }, + "$dynamicAnchor": "meta", + "title": "Validation vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "type": { + "anyOf": [ + { "$ref": "#/$defs/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/$defs/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "const": true, + "enum": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/$defs/nonNegativeInteger" }, + "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": { "$ref": "#/$defs/nonNegativeInteger" }, + "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxContains": { "$ref": "#/$defs/nonNegativeInteger" }, + "minContains": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 1 + }, + "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" }, + "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/$defs/stringArray" }, + "dependentRequired": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/stringArray" + } + } + }, + "$defs": { + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 0 + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/schema b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/schema new file mode 100644 index 000000000..364f8ada6 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/metaschemas/draft/2020-12/schema @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/schema", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true, + "https://json-schema.org/draft/2020-12/vocab/applicator": true, + "https://json-schema.org/draft/2020-12/vocab/unevaluated": true, + "https://json-schema.org/draft/2020-12/vocab/validation": true, + "https://json-schema.org/draft/2020-12/vocab/meta-data": true, + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, + "https://json-schema.org/draft/2020-12/vocab/content": true + }, + "$dynamicAnchor": "meta", + "title": "Core and Validation specifications meta-schema", + "allOf": [ + {"$ref": "meta/core"}, + {"$ref": "meta/applicator"}, + {"$ref": "meta/unevaluated"}, + {"$ref": "meta/validation"}, + {"$ref": "meta/meta-data"}, + {"$ref": "meta/format-annotation"}, + {"$ref": "meta/content"} + ], + "type": ["object", "boolean"], + "$comment": "This meta-schema also defines keywords that have appeared in previous drafts in order to prevent incompatible extensions as they remain in common use.", + "properties": { + "definitions": { + "$comment": "\"definitions\" has been replaced by \"$defs\".", + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" }, + "deprecated": true, + "default": {} + }, + "dependencies": { + "$comment": "\"dependencies\" has been split and replaced by \"dependentSchemas\" and \"dependentRequired\" in order to serve their differing semantics.", + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$dynamicRef": "#meta" }, + { "$ref": "meta/validation#/$defs/stringArray" } + ] + }, + "deprecated": true, + "default": {} + }, + "$recursiveAnchor": { + "$comment": "\"$recursiveAnchor\" has been replaced by \"$dynamicAnchor\".", + "$ref": "meta/core#/$defs/anchorString", + "deprecated": true + }, + "$recursiveRef": { + "$comment": "\"$recursiveRef\" has been replaced by \"$dynamicRef\".", + "$ref": "meta/core#/$defs/uriReferenceString", + "deprecated": true + } + } +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/objcompiler.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/objcompiler.go new file mode 100644 index 000000000..f1494b13a --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/objcompiler.go @@ -0,0 +1,549 @@ +package jsonschema + +import ( + "encoding/json" + "fmt" + "math/big" + "strconv" +) + +type objCompiler struct { + c *Compiler + obj map[string]any + up urlPtr + r *root + res *resource + q *queue +} + +func (c *objCompiler) compile(s *Schema) error { + // id -- + if id := c.res.dialect.draft.getID(c.obj); id != "" { + s.ID = id + } + + // anchor -- + if s.DraftVersion < 2019 { + // anchor is specified in id + id := c.string(c.res.dialect.draft.id) + if id != "" { + _, f := split(id) + if f != "" { + var err error + s.Anchor, err = decode(f) + if err != nil { + return &ParseAnchorError{URL: s.Location} + } + } + } + } else { + s.Anchor = c.string("$anchor") + } + + if err := c.compileDraft4(s); err != nil { + return err + } + if s.DraftVersion >= 6 { + if err := c.compileDraft6(s); err != nil { + return err + } + } + if s.DraftVersion >= 7 { + if err := c.compileDraft7(s); err != nil { + return err + } + } + if s.DraftVersion >= 2019 { + if err := c.compileDraft2019(s); err != nil { + return err + } + } + if s.DraftVersion >= 2020 { + if err := c.compileDraft2020(s); err != nil { + return err + } + } + + // vocabularies + vocabs := c.res.dialect.activeVocabs(c.c.roots.assertVocabs, c.c.roots.vocabularies) + for _, vocab := range vocabs { + v := c.c.roots.vocabularies[vocab] + if v == nil { + continue + } + ext, err := v.Compile(&CompilerContext{c}, c.obj) + if err != nil { + return err + } + if ext != nil { + s.Extensions = append(s.Extensions, ext) + } + } + + return nil +} + +func (c *objCompiler) compileDraft4(s *Schema) error { + var err error + + if c.hasVocab("core") { + if s.Ref, err = c.enqueueRef("$ref"); err != nil { + return err + } + if s.DraftVersion < 2019 && s.Ref != nil { + // All other properties in a "$ref" object MUST be ignored + return nil + } + } + + if c.hasVocab("applicator") { + s.AllOf = c.enqueueArr("allOf") + s.AnyOf = c.enqueueArr("anyOf") + s.OneOf = c.enqueueArr("oneOf") + s.Not = c.enqueueProp("not") + + if s.DraftVersion < 2020 { + if items, ok := c.obj["items"]; ok { + if _, ok := items.([]any); ok { + s.Items = c.enqueueArr("items") + s.AdditionalItems = c.enqueueAdditional("additionalItems") + } else { + s.Items = c.enqueueProp("items") + } + } + } + + s.Properties = c.enqueueMap("properties") + if m := c.enqueueMap("patternProperties"); m != nil { + s.PatternProperties = map[Regexp]*Schema{} + for pname, sch := range m { + re, err := c.c.roots.regexpEngine(pname) + if err != nil { + return &InvalidRegexError{c.up.format("patternProperties"), pname, err} + } + s.PatternProperties[re] = sch + } + } + s.AdditionalProperties = c.enqueueAdditional("additionalProperties") + + if m := c.objVal("dependencies"); m != nil { + s.Dependencies = map[string]any{} + for pname, pvalue := range m { + if arr, ok := pvalue.([]any); ok { + s.Dependencies[pname] = toStrings(arr) + } else { + ptr := c.up.ptr.append2("dependencies", pname) + s.Dependencies[pname] = c.enqueuePtr(ptr) + } + } + } + } + + if c.hasVocab("validation") { + if t, ok := c.obj["type"]; ok { + s.Types = newTypes(t) + } + if arr := c.arrVal("enum"); arr != nil { + s.Enum = newEnum(arr) + } + s.MultipleOf = c.numVal("multipleOf") + s.Maximum = c.numVal("maximum") + if c.boolean("exclusiveMaximum") { + s.ExclusiveMaximum = s.Maximum + s.Maximum = nil + } else { + s.ExclusiveMaximum = c.numVal("exclusiveMaximum") + } + s.Minimum = c.numVal("minimum") + if c.boolean("exclusiveMinimum") { + s.ExclusiveMinimum = s.Minimum + s.Minimum = nil + } else { + s.ExclusiveMinimum = c.numVal("exclusiveMinimum") + } + + s.MinLength = c.intVal("minLength") + s.MaxLength = c.intVal("maxLength") + if pat := c.strVal("pattern"); pat != nil { + s.Pattern, err = c.c.roots.regexpEngine(*pat) + if err != nil { + return &InvalidRegexError{c.up.format("pattern"), *pat, err} + } + } + + s.MinItems = c.intVal("minItems") + s.MaxItems = c.intVal("maxItems") + s.UniqueItems = c.boolean("uniqueItems") + + s.MaxProperties = c.intVal("maxProperties") + s.MinProperties = c.intVal("minProperties") + if arr := c.arrVal("required"); arr != nil { + s.Required = toStrings(arr) + } + } + + // format -- + if c.assertFormat(s.DraftVersion) { + if f := c.strVal("format"); f != nil { + if *f == "regex" { + s.Format = &Format{ + Name: "regex", + Validate: c.c.roots.regexpEngine.validate, + } + } else { + s.Format = c.c.formats[*f] + if s.Format == nil { + s.Format = formats[*f] + } + } + } + } + + // annotations -- + s.Title = c.string("title") + s.Description = c.string("description") + if v, ok := c.obj["default"]; ok { + s.Default = &v + } + + return nil +} + +func (c *objCompiler) compileDraft6(s *Schema) error { + if c.hasVocab("applicator") { + s.Contains = c.enqueueProp("contains") + s.PropertyNames = c.enqueueProp("propertyNames") + } + if c.hasVocab("validation") { + if v, ok := c.obj["const"]; ok { + s.Const = &v + } + } + return nil +} + +func (c *objCompiler) compileDraft7(s *Schema) error { + if c.hasVocab("applicator") { + s.If = c.enqueueProp("if") + if s.If != nil { + b := c.boolVal("if") + if b == nil || *b { + s.Then = c.enqueueProp("then") + } + if b == nil || !*b { + s.Else = c.enqueueProp("else") + } + } + } + + if c.c.assertContent { + if ce := c.strVal("contentEncoding"); ce != nil { + s.ContentEncoding = c.c.decoders[*ce] + if s.ContentEncoding == nil { + s.ContentEncoding = decoders[*ce] + } + } + if cm := c.strVal("contentMediaType"); cm != nil { + s.ContentMediaType = c.c.mediaTypes[*cm] + if s.ContentMediaType == nil { + s.ContentMediaType = mediaTypes[*cm] + } + } + } + + // annotations -- + s.Comment = c.string("$comment") + s.ReadOnly = c.boolean("readOnly") + s.WriteOnly = c.boolean("writeOnly") + if arr, ok := c.obj["examples"].([]any); ok { + s.Examples = arr + } + + return nil +} + +func (c *objCompiler) compileDraft2019(s *Schema) error { + var err error + + if c.hasVocab("core") { + if s.RecursiveRef, err = c.enqueueRef("$recursiveRef"); err != nil { + return err + } + s.RecursiveAnchor = c.boolean("$recursiveAnchor") + } + + if c.hasVocab("validation") { + if s.Contains != nil { + s.MinContains = c.intVal("minContains") + s.MaxContains = c.intVal("maxContains") + } + if m := c.objVal("dependentRequired"); m != nil { + s.DependentRequired = map[string][]string{} + for pname, pvalue := range m { + if arr, ok := pvalue.([]any); ok { + s.DependentRequired[pname] = toStrings(arr) + } + } + } + } + + if c.hasVocab("applicator") { + s.DependentSchemas = c.enqueueMap("dependentSchemas") + } + + var unevaluated bool + if s.DraftVersion == 2019 { + unevaluated = c.hasVocab("applicator") + } else { + unevaluated = c.hasVocab("unevaluated") + } + if unevaluated { + s.UnevaluatedItems = c.enqueueProp("unevaluatedItems") + s.UnevaluatedProperties = c.enqueueProp("unevaluatedProperties") + } + + if c.c.assertContent { + if s.ContentMediaType != nil && s.ContentMediaType.UnmarshalJSON != nil { + s.ContentSchema = c.enqueueProp("contentSchema") + } + } + + // annotations -- + s.Deprecated = c.boolean("deprecated") + + return nil +} + +func (c *objCompiler) compileDraft2020(s *Schema) error { + if c.hasVocab("core") { + sch, err := c.enqueueRef("$dynamicRef") + if err != nil { + return err + } + if sch != nil { + dref := c.strVal("$dynamicRef") + _, frag, err := splitFragment(*dref) + if err != nil { + return err + } + var anch string + if anchor, ok := frag.convert().(anchor); ok { + anch = string(anchor) + } + s.DynamicRef = &DynamicRef{sch, anch} + } + s.DynamicAnchor = c.string("$dynamicAnchor") + } + + if c.hasVocab("applicator") { + s.PrefixItems = c.enqueueArr("prefixItems") + s.Items2020 = c.enqueueProp("items") + } + + return nil +} + +// enqueue helpers -- + +func (c *objCompiler) enqueuePtr(ptr jsonPointer) *Schema { + up := urlPtr{c.up.url, ptr} + return c.c.enqueue(c.q, up) +} + +func (c *objCompiler) enqueueRef(pname string) (*Schema, error) { + ref := c.strVal(pname) + if ref == nil { + return nil, nil + } + baseURL := c.res.id + // baseURL := c.r.baseURL(c.up.ptr) + uf, err := baseURL.join(*ref) + if err != nil { + return nil, err + } + + up, err := c.r.resolve(*uf) + if err != nil { + return nil, err + } + if up != nil { + // local ref + return c.enqueuePtr(up.ptr), nil + } + + // remote ref + up_, err := c.c.roots.resolveFragment(*uf) + if err != nil { + return nil, err + } + return c.c.enqueue(c.q, up_), nil +} + +func (c *objCompiler) enqueueProp(pname string) *Schema { + if _, ok := c.obj[pname]; !ok { + return nil + } + ptr := c.up.ptr.append(pname) + return c.enqueuePtr(ptr) +} + +func (c *objCompiler) enqueueArr(pname string) []*Schema { + arr := c.arrVal(pname) + if arr == nil { + return nil + } + sch := make([]*Schema, len(arr)) + for i := range arr { + ptr := c.up.ptr.append2(pname, strconv.Itoa(i)) + sch[i] = c.enqueuePtr(ptr) + } + return sch +} + +func (c *objCompiler) enqueueMap(pname string) map[string]*Schema { + obj := c.objVal(pname) + if obj == nil { + return nil + } + sch := make(map[string]*Schema) + for k := range obj { + ptr := c.up.ptr.append2(pname, k) + sch[k] = c.enqueuePtr(ptr) + } + return sch +} + +func (c *objCompiler) enqueueAdditional(pname string) any { + if b := c.boolVal(pname); b != nil { + return *b + } + if sch := c.enqueueProp(pname); sch != nil { + return sch + } + return nil +} + +// -- + +func (c *objCompiler) hasVocab(name string) bool { + return c.res.dialect.hasVocab(name) +} + +func (c *objCompiler) assertFormat(draftVersion int) bool { + if c.c.assertFormat || draftVersion < 2019 { + return true + } + if draftVersion == 2019 { + return c.hasVocab("format") + } else { + return c.hasVocab("format-assertion") + } +} + +// value helpers -- + +func (c *objCompiler) boolVal(pname string) *bool { + v, ok := c.obj[pname] + if !ok { + return nil + } + b, ok := v.(bool) + if !ok { + return nil + } + return &b +} + +func (c *objCompiler) boolean(pname string) bool { + b := c.boolVal(pname) + return b != nil && *b +} + +func (c *objCompiler) strVal(pname string) *string { + v, ok := c.obj[pname] + if !ok { + return nil + } + s, ok := v.(string) + if !ok { + return nil + } + return &s +} + +func (c *objCompiler) string(pname string) string { + if s := c.strVal(pname); s != nil { + return *s + } + return "" +} + +func (c *objCompiler) numVal(pname string) *big.Rat { + v, ok := c.obj[pname] + if !ok { + return nil + } + switch v.(type) { + case json.Number, float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + if n, ok := new(big.Rat).SetString(fmt.Sprint(v)); ok { + return n + } + } + return nil +} + +func (c *objCompiler) intVal(pname string) *int { + if n := c.numVal(pname); n != nil && n.IsInt() { + n := int(n.Num().Int64()) + return &n + } + return nil +} + +func (c *objCompiler) objVal(pname string) map[string]any { + v, ok := c.obj[pname] + if !ok { + return nil + } + obj, ok := v.(map[string]any) + if !ok { + return nil + } + return obj +} + +func (c *objCompiler) arrVal(pname string) []any { + v, ok := c.obj[pname] + if !ok { + return nil + } + arr, ok := v.([]any) + if !ok { + return nil + } + return arr +} + +// -- + +type InvalidRegexError struct { + URL string + Regex string + Err error +} + +func (e *InvalidRegexError) Error() string { + return fmt.Sprintf("invalid regex %q at %q: %v", e.Regex, e.URL, e.Err) +} + +// -- + +func toStrings(arr []any) []string { + var strings []string + for _, item := range arr { + if s, ok := item.(string); ok { + strings = append(strings, s) + } + } + return strings +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/output.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/output.go new file mode 100644 index 000000000..69d3f26de --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/output.go @@ -0,0 +1,216 @@ +package jsonschema + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/santhosh-tekuri/jsonschema/v6/kind" + "golang.org/x/text/language" + "golang.org/x/text/message" +) + +var defaultPrinter = message.NewPrinter(language.English) + +// format --- + +func (e *ValidationError) schemaURL() string { + if ref, ok := e.ErrorKind.(*kind.Reference); ok { + return ref.URL + } else { + return e.SchemaURL + } +} + +func (e *ValidationError) absoluteKeywordLocation() string { + var schemaURL string + var keywordPath []string + if ref, ok := e.ErrorKind.(*kind.Reference); ok { + schemaURL = ref.URL + keywordPath = nil + } else { + schemaURL = e.SchemaURL + keywordPath = e.ErrorKind.KeywordPath() + } + return fmt.Sprintf("%s%s", schemaURL, encode(jsonPtr(keywordPath))) +} + +func (e *ValidationError) skip() bool { + if len(e.Causes) == 1 { + _, ok := e.ErrorKind.(*kind.Reference) + return ok + } + return false +} + +func (e *ValidationError) display(sb *strings.Builder, verbose bool, indent int, absKwLoc string, p *message.Printer) { + if !e.skip() { + if indent > 0 { + sb.WriteByte('\n') + for i := 0; i < indent-1; i++ { + sb.WriteString(" ") + } + sb.WriteString("- ") + } + indent = indent + 1 + + prevAbsKwLoc := absKwLoc + absKwLoc = e.absoluteKeywordLocation() + + if _, ok := e.ErrorKind.(*kind.Schema); ok { + sb.WriteString(e.ErrorKind.LocalizedString(p)) + } else { + sb.WriteString(p.Sprintf("at %s", quote(jsonPtr(e.InstanceLocation)))) + if verbose { + schLoc := absKwLoc + if prevAbsKwLoc != "" { + pu, _ := split(prevAbsKwLoc) + u, f := split(absKwLoc) + if u == pu { + schLoc = fmt.Sprintf("S#%s", f) + } + } + fmt.Fprintf(sb, " [%s]", schLoc) + } + fmt.Fprintf(sb, ": %s", e.ErrorKind.LocalizedString(p)) + } + } + for _, cause := range e.Causes { + cause.display(sb, verbose, indent, absKwLoc, p) + } +} + +func (e *ValidationError) Error() string { + return e.LocalizedError(defaultPrinter) +} + +func (e *ValidationError) LocalizedError(p *message.Printer) string { + var sb strings.Builder + e.display(&sb, false, 0, "", p) + return sb.String() +} + +func (e *ValidationError) GoString() string { + return e.LocalizedGoString(defaultPrinter) +} + +func (e *ValidationError) LocalizedGoString(p *message.Printer) string { + var sb strings.Builder + e.display(&sb, true, 0, "", p) + return sb.String() +} + +func jsonPtr(tokens []string) string { + var sb strings.Builder + for _, tok := range tokens { + sb.WriteByte('/') + sb.WriteString(escape(tok)) + } + return sb.String() +} + +// -- + +// Flag is output format with simple boolean property valid. +type FlagOutput struct { + Valid bool `json:"valid"` +} + +// The `Flag` output format, merely the boolean result. +func (e *ValidationError) FlagOutput() *FlagOutput { + return &FlagOutput{Valid: false} +} + +// -- + +type OutputUnit struct { + Valid bool `json:"valid"` + KeywordLocation string `json:"keywordLocation"` + AbsoluteKeywordLocation string `json:"AbsoluteKeywordLocation,omitempty"` + InstanceLocation string `json:"instanceLocation"` + Error *OutputError `json:"error,omitempty"` + Errors []OutputUnit `json:"errors,omitempty"` +} + +type OutputError struct { + Kind ErrorKind + p *message.Printer +} + +func (k OutputError) String() string { + return k.Kind.LocalizedString(k.p) +} + +func (k OutputError) MarshalJSON() ([]byte, error) { + return json.Marshal(k.Kind.LocalizedString(k.p)) +} + +// The `Basic` structure, a flat list of output units. +func (e *ValidationError) BasicOutput() *OutputUnit { + return e.LocalizedBasicOutput(defaultPrinter) +} + +func (e *ValidationError) LocalizedBasicOutput(p *message.Printer) *OutputUnit { + out := e.output(true, false, "", "", p) + return &out +} + +// The `Detailed` structure, based on the schema. +func (e *ValidationError) DetailedOutput() *OutputUnit { + return e.LocalizedDetailedOutput(defaultPrinter) +} + +func (e *ValidationError) LocalizedDetailedOutput(p *message.Printer) *OutputUnit { + out := e.output(false, false, "", "", p) + return &out +} + +func (e *ValidationError) output(flatten, inRef bool, schemaURL, kwLoc string, p *message.Printer) OutputUnit { + if !inRef { + if _, ok := e.ErrorKind.(*kind.Reference); ok { + inRef = true + } + } + if schemaURL != "" { + kwLoc += e.SchemaURL[len(schemaURL):] + if ref, ok := e.ErrorKind.(*kind.Reference); ok { + kwLoc += jsonPtr(ref.KeywordPath()) + } + } + schemaURL = e.schemaURL() + + keywordLocation := kwLoc + if _, ok := e.ErrorKind.(*kind.Reference); !ok { + keywordLocation += jsonPtr(e.ErrorKind.KeywordPath()) + } + + out := OutputUnit{ + Valid: false, + InstanceLocation: jsonPtr(e.InstanceLocation), + KeywordLocation: keywordLocation, + } + if inRef { + out.AbsoluteKeywordLocation = e.absoluteKeywordLocation() + } + for _, cause := range e.Causes { + causeOut := cause.output(flatten, inRef, schemaURL, kwLoc, p) + if cause.skip() { + causeOut = causeOut.Errors[0] + } + if flatten { + errors := causeOut.Errors + causeOut.Errors = nil + causeOut.Error = &OutputError{cause.ErrorKind, p} + out.Errors = append(out.Errors, causeOut) + if len(errors) > 0 { + out.Errors = append(out.Errors, errors...) + } + } else { + out.Errors = append(out.Errors, causeOut) + } + } + if len(out.Errors) == 0 { + out.Error = &OutputError{e.ErrorKind, p} + } + return out +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/position.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/position.go new file mode 100644 index 000000000..576a2a47f --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/position.go @@ -0,0 +1,142 @@ +package jsonschema + +import ( + "strconv" + "strings" +) + +// Position tells possible tokens in json. +type Position interface { + collect(v any, ptr jsonPointer) map[jsonPointer]any +} + +// -- + +type AllProp struct{} + +func (AllProp) collect(v any, ptr jsonPointer) map[jsonPointer]any { + obj, ok := v.(map[string]any) + if !ok { + return nil + } + m := map[jsonPointer]any{} + for pname, pvalue := range obj { + m[ptr.append(pname)] = pvalue + } + return m +} + +// -- + +type AllItem struct{} + +func (AllItem) collect(v any, ptr jsonPointer) map[jsonPointer]any { + arr, ok := v.([]any) + if !ok { + return nil + } + m := map[jsonPointer]any{} + for i, item := range arr { + m[ptr.append(strconv.Itoa(i))] = item + } + return m +} + +// -- + +type Prop string + +func (p Prop) collect(v any, ptr jsonPointer) map[jsonPointer]any { + obj, ok := v.(map[string]any) + if !ok { + return nil + } + pvalue, ok := obj[string(p)] + if !ok { + return nil + } + return map[jsonPointer]any{ + ptr.append(string(p)): pvalue, + } +} + +// -- + +type Item int + +func (i Item) collect(v any, ptr jsonPointer) map[jsonPointer]any { + arr, ok := v.([]any) + if !ok { + return nil + } + if i < 0 || int(i) >= len(arr) { + return nil + } + return map[jsonPointer]any{ + ptr.append(strconv.Itoa(int(i))): arr[int(i)], + } +} + +// -- + +// SchemaPath tells where to look for subschema inside keyword. +type SchemaPath []Position + +func schemaPath(path string) SchemaPath { + var sp SchemaPath + for _, tok := range strings.Split(path, "/") { + var pos Position + switch tok { + case "*": + pos = AllProp{} + case "[]": + pos = AllItem{} + default: + if i, err := strconv.Atoi(tok); err == nil { + pos = Item(i) + } else { + pos = Prop(tok) + } + } + sp = append(sp, pos) + } + return sp +} + +func (sp SchemaPath) collect(v any, ptr jsonPointer) map[jsonPointer]any { + if len(sp) == 0 { + return map[jsonPointer]any{ + ptr: v, + } + } + p, sp := sp[0], sp[1:] + m := p.collect(v, ptr) + mm := map[jsonPointer]any{} + for ptr, v := range m { + m = sp.collect(v, ptr) + for k, v := range m { + mm[k] = v + } + } + return mm +} + +func (sp SchemaPath) String() string { + var sb strings.Builder + for _, pos := range sp { + if sb.Len() != 0 { + sb.WriteByte('/') + } + switch pos := pos.(type) { + case AllProp: + sb.WriteString("*") + case AllItem: + sb.WriteString("[]") + case Prop: + sb.WriteString(string(pos)) + case Item: + sb.WriteString(strconv.Itoa(int(pos))) + } + } + return sb.String() +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/root.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/root.go new file mode 100644 index 000000000..a8b819bab --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/root.go @@ -0,0 +1,202 @@ +package jsonschema + +import ( + "fmt" + "slices" + "strings" +) + +type root struct { + url url + doc any + resources map[jsonPointer]*resource + subschemasProcessed map[jsonPointer]struct{} +} + +func (r *root) rootResource() *resource { + return r.resources[""] +} + +func (r *root) resource(ptr jsonPointer) *resource { + for { + if res, ok := r.resources[ptr]; ok { + return res + } + slash := strings.LastIndexByte(string(ptr), '/') + if slash == -1 { + break + } + ptr = ptr[:slash] + } + return r.rootResource() +} + +func (r *root) resolveFragmentIn(frag fragment, res *resource) (urlPtr, error) { + var ptr jsonPointer + switch f := frag.convert().(type) { + case jsonPointer: + ptr = res.ptr.concat(f) + case anchor: + aptr, ok := res.anchors[f] + if !ok { + return urlPtr{}, &AnchorNotFoundError{ + URL: r.url.String(), + Reference: (&urlFrag{res.id, frag}).String(), + } + } + ptr = aptr + } + return urlPtr{r.url, ptr}, nil +} + +func (r *root) resolveFragment(frag fragment) (urlPtr, error) { + return r.resolveFragmentIn(frag, r.rootResource()) +} + +// resolves urlFrag to urlPtr from root. +// returns nil if it is external. +func (r *root) resolve(uf urlFrag) (*urlPtr, error) { + var res *resource + if uf.url == r.url { + res = r.rootResource() + } else { + // look for resource with id==uf.url + for _, v := range r.resources { + if v.id == uf.url { + res = v + break + } + } + if res == nil { + return nil, nil // external url + } + } + up, err := r.resolveFragmentIn(uf.frag, res) + return &up, err +} + +func (r *root) collectAnchors(sch any, schPtr jsonPointer, res *resource) error { + obj, ok := sch.(map[string]any) + if !ok { + return nil + } + + addAnchor := func(anchor anchor) error { + ptr1, ok := res.anchors[anchor] + if ok { + if ptr1 == schPtr { + // anchor with same root_ptr already exists + return nil + } + return &DuplicateAnchorError{ + string(anchor), r.url.String(), string(ptr1), string(schPtr), + } + } + res.anchors[anchor] = schPtr + return nil + } + + if res.dialect.draft.version < 2019 { + if _, ok := obj["$ref"]; ok { + // All other properties in a "$ref" object MUST be ignored + return nil + } + // anchor is specified in id + if id, ok := strVal(obj, res.dialect.draft.id); ok { + _, frag, err := splitFragment(id) + if err != nil { + loc := urlPtr{r.url, schPtr} + return &ParseAnchorError{loc.String()} + } + if anchor, ok := frag.convert().(anchor); ok { + if err := addAnchor(anchor); err != nil { + return err + } + } + } + } + if res.dialect.draft.version >= 2019 { + if s, ok := strVal(obj, "$anchor"); ok { + if err := addAnchor(anchor(s)); err != nil { + return err + } + } + } + if res.dialect.draft.version >= 2020 { + if s, ok := strVal(obj, "$dynamicAnchor"); ok { + if err := addAnchor(anchor(s)); err != nil { + return err + } + res.dynamicAnchors = append(res.dynamicAnchors, anchor(s)) + } + } + + return nil +} + +func (r *root) clone() *root { + processed := map[jsonPointer]struct{}{} + for k := range r.subschemasProcessed { + processed[k] = struct{}{} + } + resources := map[jsonPointer]*resource{} + for k, v := range r.resources { + resources[k] = v.clone() + } + return &root{ + url: r.url, + doc: r.doc, + resources: resources, + subschemasProcessed: processed, + } +} + +// -- + +type resource struct { + ptr jsonPointer + id url + dialect dialect + anchors map[anchor]jsonPointer + dynamicAnchors []anchor +} + +func newResource(ptr jsonPointer, id url) *resource { + return &resource{ptr: ptr, id: id, anchors: make(map[anchor]jsonPointer)} +} + +func (res *resource) clone() *resource { + anchors := map[anchor]jsonPointer{} + for k, v := range res.anchors { + anchors[k] = v + } + return &resource{ + ptr: res.ptr, + id: res.id, + dialect: res.dialect, + anchors: anchors, + dynamicAnchors: slices.Clone(res.dynamicAnchors), + } +} + +//-- + +type UnsupportedVocabularyError struct { + URL string + Vocabulary string +} + +func (e *UnsupportedVocabularyError) Error() string { + return fmt.Sprintf("unsupported vocabulary %q in %q", e.Vocabulary, e.URL) +} + +// -- + +type AnchorNotFoundError struct { + URL string + Reference string +} + +func (e *AnchorNotFoundError) Error() string { + return fmt.Sprintf("anchor in %q not found in schema %q", e.Reference, e.URL) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/roots.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/roots.go new file mode 100644 index 000000000..a8d0ef0ce --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/roots.go @@ -0,0 +1,286 @@ +package jsonschema + +import ( + "fmt" + "strings" +) + +type roots struct { + defaultDraft *Draft + roots map[url]*root + loader defaultLoader + regexpEngine RegexpEngine + vocabularies map[string]*Vocabulary + assertVocabs bool +} + +func newRoots() *roots { + return &roots{ + defaultDraft: draftLatest, + roots: map[url]*root{}, + loader: defaultLoader{ + docs: map[url]any{}, + loader: FileLoader{}, + }, + regexpEngine: goRegexpCompile, + vocabularies: map[string]*Vocabulary{}, + } +} + +func (rr *roots) orLoad(u url) (*root, error) { + if r, ok := rr.roots[u]; ok { + return r, nil + } + doc, err := rr.loader.load(u) + if err != nil { + return nil, err + } + return rr.addRoot(u, doc) +} + +func (rr *roots) addRoot(u url, doc any) (*root, error) { + r := &root{ + url: u, + doc: doc, + resources: map[jsonPointer]*resource{}, + subschemasProcessed: map[jsonPointer]struct{}{}, + } + if err := rr.collectResources(r, doc, u, "", dialect{rr.defaultDraft, nil}); err != nil { + return nil, err + } + if !strings.HasPrefix(u.String(), "http://json-schema.org/") && + !strings.HasPrefix(u.String(), "https://json-schema.org/") { + if err := rr.validate(r, doc, ""); err != nil { + return nil, err + } + } + + rr.roots[u] = r + return r, nil +} + +func (rr *roots) resolveFragment(uf urlFrag) (urlPtr, error) { + r, err := rr.orLoad(uf.url) + if err != nil { + return urlPtr{}, err + } + return r.resolveFragment(uf.frag) +} + +func (rr *roots) collectResources(r *root, sch any, base url, schPtr jsonPointer, fallback dialect) error { + if _, ok := r.subschemasProcessed[schPtr]; ok { + return nil + } + if err := rr._collectResources(r, sch, base, schPtr, fallback); err != nil { + return err + } + r.subschemasProcessed[schPtr] = struct{}{} + return nil +} + +func (rr *roots) _collectResources(r *root, sch any, base url, schPtr jsonPointer, fallback dialect) error { + obj, ok := sch.(map[string]any) + if !ok { + if schPtr.isEmpty() { + // root resource + res := newResource(schPtr, base) + res.dialect = fallback + r.resources[schPtr] = res + } + return nil + } + + hasSchema := false + if sch, ok := obj["$schema"]; ok { + if _, ok := sch.(string); ok { + hasSchema = true + } + } + + draft, err := rr.loader.getDraft(urlPtr{r.url, schPtr}, sch, fallback.draft, map[url]struct{}{}) + if err != nil { + return err + } + id := draft.getID(obj) + if id == "" && !schPtr.isEmpty() { + // ignore $schema + draft = fallback.draft + hasSchema = false + id = draft.getID(obj) + } + + var res *resource + if id != "" { + uf, err := base.join(id) + if err != nil { + loc := urlPtr{r.url, schPtr} + return &ParseIDError{loc.String()} + } + base = uf.url + res = newResource(schPtr, base) + } else if schPtr.isEmpty() { + // root resource + res = newResource(schPtr, base) + } + + if res != nil { + found := false + for _, res := range r.resources { + if res.id == base { + found = true + if res.ptr != schPtr { + return &DuplicateIDError{base.String(), r.url.String(), string(schPtr), string(res.ptr)} + } + } + } + if !found { + if hasSchema { + vocabs, err := rr.loader.getMetaVocabs(sch, draft, rr.vocabularies) + if err != nil { + return err + } + res.dialect = dialect{draft, vocabs} + } else { + res.dialect = fallback + } + r.resources[schPtr] = res + } + } + + var baseRes *resource + for _, res := range r.resources { + if res.id == base { + baseRes = res + break + } + } + if baseRes == nil { + panic("baseres is nil") + } + + // found base resource + if err := r.collectAnchors(sch, schPtr, baseRes); err != nil { + return err + } + + // process subschemas + subschemas := map[jsonPointer]any{} + for _, sp := range draft.subschemas { + ss := sp.collect(obj, schPtr) + for k, v := range ss { + subschemas[k] = v + } + } + for _, vocab := range baseRes.dialect.activeVocabs(true, rr.vocabularies) { + if v := rr.vocabularies[vocab]; v != nil { + for _, sp := range v.Subschemas { + ss := sp.collect(obj, schPtr) + for k, v := range ss { + subschemas[k] = v + } + } + } + } + for ptr, v := range subschemas { + if err := rr.collectResources(r, v, base, ptr, baseRes.dialect); err != nil { + return err + } + } + + return nil +} + +func (rr *roots) ensureSubschema(up urlPtr) error { + r, err := rr.orLoad(up.url) + if err != nil { + return err + } + if _, ok := r.subschemasProcessed[up.ptr]; ok { + return nil + } + v, err := up.lookup(r.doc) + if err != nil { + return err + } + rClone := r.clone() + if err := rr.addSubschema(rClone, up.ptr); err != nil { + return err + } + if err := rr.validate(rClone, v, up.ptr); err != nil { + return err + } + rr.roots[r.url] = rClone + return nil +} + +func (rr *roots) addSubschema(r *root, ptr jsonPointer) error { + v, err := (&urlPtr{r.url, ptr}).lookup(r.doc) + if err != nil { + return err + } + base := r.resource(ptr) + baseURL := base.id + if err := rr.collectResources(r, v, baseURL, ptr, base.dialect); err != nil { + return err + } + + // collect anchors + if _, ok := r.resources[ptr]; !ok { + res := r.resource(ptr) + if err := r.collectAnchors(v, ptr, res); err != nil { + return err + } + } + return nil +} + +func (rr *roots) validate(r *root, v any, ptr jsonPointer) error { + dialect := r.resource(ptr).dialect + meta := dialect.getSchema(rr.assertVocabs, rr.vocabularies) + if err := meta.validate(v, rr.regexpEngine, meta, r.resources, rr.assertVocabs, rr.vocabularies); err != nil { + up := urlPtr{r.url, ptr} + return &SchemaValidationError{URL: up.String(), Err: err} + } + return nil +} + +// -- + +type InvalidMetaSchemaURLError struct { + URL string + Err error +} + +func (e *InvalidMetaSchemaURLError) Error() string { + return fmt.Sprintf("invalid $schema in %q: %v", e.URL, e.Err) +} + +// -- + +type UnsupportedDraftError struct { + URL string +} + +func (e *UnsupportedDraftError) Error() string { + return fmt.Sprintf("draft %q is not supported", e.URL) +} + +// -- + +type MetaSchemaCycleError struct { + URL string +} + +func (e *MetaSchemaCycleError) Error() string { + return fmt.Sprintf("cycle in resolving $schema in %q", e.URL) +} + +// -- + +type MetaSchemaMismatchError struct { + URL string +} + +func (e *MetaSchemaMismatchError) Error() string { + return fmt.Sprintf("$schema in %q does not match with $schema in root", e.URL) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/schema.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/schema.go new file mode 100644 index 000000000..b4c1f37af --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/schema.go @@ -0,0 +1,254 @@ +package jsonschema + +import ( + "encoding/json" + "fmt" + "math/big" +) + +// Schema is the representation of a compiled +// jsonschema. +type Schema struct { + up urlPtr + resource *Schema + dynamicAnchors map[string]*Schema + allPropsEvaluated bool + allItemsEvaluated bool + numItemsEvaluated int + + DraftVersion int + Location string + + // type agnostic -- + Bool *bool // boolean schema + ID string + Ref *Schema + Anchor string + RecursiveRef *Schema + RecursiveAnchor bool + DynamicRef *DynamicRef + DynamicAnchor string // "" if not specified + Types *Types + Enum *Enum + Const *any + Not *Schema + AllOf []*Schema + AnyOf []*Schema + OneOf []*Schema + If *Schema + Then *Schema + Else *Schema + Format *Format + + // object -- + MaxProperties *int + MinProperties *int + Required []string + PropertyNames *Schema + Properties map[string]*Schema + PatternProperties map[Regexp]*Schema + AdditionalProperties any // nil or bool or *Schema + Dependencies map[string]any // value is []string or *Schema + DependentRequired map[string][]string + DependentSchemas map[string]*Schema + UnevaluatedProperties *Schema + + // array -- + MinItems *int + MaxItems *int + UniqueItems bool + Contains *Schema + MinContains *int + MaxContains *int + Items any // nil or []*Schema or *Schema + AdditionalItems any // nil or bool or *Schema + PrefixItems []*Schema + Items2020 *Schema + UnevaluatedItems *Schema + + // string -- + MinLength *int + MaxLength *int + Pattern Regexp + ContentEncoding *Decoder + ContentMediaType *MediaType + ContentSchema *Schema + + // number -- + Maximum *big.Rat + Minimum *big.Rat + ExclusiveMaximum *big.Rat + ExclusiveMinimum *big.Rat + MultipleOf *big.Rat + + Extensions []SchemaExt + + // annotations -- + Title string + Description string + Default *any + Comment string + ReadOnly bool + WriteOnly bool + Examples []any + Deprecated bool +} + +// -- + +type jsonType int + +const ( + invalidType jsonType = 0 + nullType jsonType = 1 << iota + booleanType + numberType + integerType + stringType + arrayType + objectType +) + +func typeOf(v any) jsonType { + switch v.(type) { + case nil: + return nullType + case bool: + return booleanType + case json.Number, float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return numberType + case string: + return stringType + case []any: + return arrayType + case map[string]any: + return objectType + default: + return invalidType + } +} + +func typeFromString(s string) jsonType { + switch s { + case "null": + return nullType + case "boolean": + return booleanType + case "number": + return numberType + case "integer": + return integerType + case "string": + return stringType + case "array": + return arrayType + case "object": + return objectType + } + return invalidType +} + +func (jt jsonType) String() string { + switch jt { + case nullType: + return "null" + case booleanType: + return "boolean" + case numberType: + return "number" + case integerType: + return "integer" + case stringType: + return "string" + case arrayType: + return "array" + case objectType: + return "object" + } + return "" +} + +// -- + +// Types encapsulates list of json value types. +type Types int + +func newTypes(v any) *Types { + var types Types + switch v := v.(type) { + case string: + types.Add(v) + case []any: + for _, item := range v { + if s, ok := item.(string); ok { + types.Add(s) + } + } + } + if types.IsEmpty() { + return nil + } + return &types +} + +func (tt Types) IsEmpty() bool { + return tt == 0 +} + +// Add specified json type. If typ is +// not valid json type it is ignored. +func (tt *Types) Add(typ string) { + tt.add(typeFromString(typ)) +} + +func (tt *Types) add(t jsonType) { + *tt = Types(int(*tt) | int(t)) +} + +func (tt Types) contains(t jsonType) bool { + return int(tt)&int(t) != 0 +} + +func (tt Types) ToStrings() []string { + types := []jsonType{ + nullType, booleanType, numberType, integerType, + stringType, arrayType, objectType, + } + var arr []string + for _, t := range types { + if tt.contains(t) { + arr = append(arr, t.String()) + } + } + return arr +} + +func (tt Types) String() string { + return fmt.Sprintf("%v", tt.ToStrings()) +} + +// -- + +type Enum struct { + Values []any + types Types +} + +func newEnum(arr []any) *Enum { + var types Types + for _, item := range arr { + types.add(typeOf(item)) + } + return &Enum{arr, types} +} + +// -- + +type DynamicRef struct { + Ref *Schema + Anchor string // "" if not specified +} + +func newSchema(up urlPtr) *Schema { + return &Schema{up: up, Location: up.String()} +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/util.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/util.go new file mode 100644 index 000000000..c6f8e7752 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/util.go @@ -0,0 +1,464 @@ +package jsonschema + +import ( + "encoding/json" + "fmt" + "hash/maphash" + "math/big" + gourl "net/url" + "path/filepath" + "runtime" + "slices" + "strconv" + "strings" + + "github.com/santhosh-tekuri/jsonschema/v6/kind" + "golang.org/x/text/message" +) + +// -- + +type url (string) + +func (u url) String() string { + return string(u) +} + +func (u url) join(ref string) (*urlFrag, error) { + base, err := gourl.Parse(string(u)) + if err != nil { + return nil, &ParseURLError{URL: u.String(), Err: err} + } + + ref, frag, err := splitFragment(ref) + if err != nil { + return nil, err + } + refURL, err := gourl.Parse(ref) + if err != nil { + return nil, &ParseURLError{URL: ref, Err: err} + } + resolved := base.ResolveReference(refURL) + + // see https://github.com/golang/go/issues/66084 (net/url: ResolveReference ignores Opaque value) + if !refURL.IsAbs() && base.Opaque != "" { + resolved.Opaque = base.Opaque + } + + return &urlFrag{url: url(resolved.String()), frag: frag}, nil +} + +// -- + +type jsonPointer string + +func escape(tok string) string { + tok = strings.ReplaceAll(tok, "~", "~0") + tok = strings.ReplaceAll(tok, "/", "~1") + return tok +} + +func unescape(tok string) (string, bool) { + tilde := strings.IndexByte(tok, '~') + if tilde == -1 { + return tok, true + } + sb := new(strings.Builder) + for { + sb.WriteString(tok[:tilde]) + tok = tok[tilde+1:] + if tok == "" { + return "", false + } + switch tok[0] { + case '0': + sb.WriteByte('~') + case '1': + sb.WriteByte('/') + default: + return "", false + } + tok = tok[1:] + tilde = strings.IndexByte(tok, '~') + if tilde == -1 { + sb.WriteString(tok) + break + } + } + return sb.String(), true +} + +func (ptr jsonPointer) isEmpty() bool { + return string(ptr) == "" +} + +func (ptr jsonPointer) concat(next jsonPointer) jsonPointer { + return jsonPointer(fmt.Sprintf("%s%s", ptr, next)) +} + +func (ptr jsonPointer) append(tok string) jsonPointer { + return jsonPointer(fmt.Sprintf("%s/%s", ptr, escape(tok))) +} + +func (ptr jsonPointer) append2(tok1, tok2 string) jsonPointer { + return jsonPointer(fmt.Sprintf("%s/%s/%s", ptr, escape(tok1), escape(tok2))) +} + +// -- + +type anchor string + +// -- + +type fragment string + +func decode(frag string) (string, error) { + return gourl.PathUnescape(frag) +} + +// avoids escaping /. +func encode(frag string) string { + var sb strings.Builder + for i, tok := range strings.Split(frag, "/") { + if i > 0 { + sb.WriteByte('/') + } + sb.WriteString(gourl.PathEscape(tok)) + } + return sb.String() +} + +func splitFragment(str string) (string, fragment, error) { + u, f := split(str) + f, err := decode(f) + if err != nil { + return "", fragment(""), &ParseURLError{URL: str, Err: err} + } + return u, fragment(f), nil +} + +func split(str string) (string, string) { + hash := strings.IndexByte(str, '#') + if hash == -1 { + return str, "" + } + return str[:hash], str[hash+1:] +} + +func (frag fragment) convert() any { + str := string(frag) + if str == "" || strings.HasPrefix(str, "/") { + return jsonPointer(str) + } + return anchor(str) +} + +// -- + +type urlFrag struct { + url url + frag fragment +} + +func startsWithWindowsDrive(s string) bool { + if s != "" && strings.HasPrefix(s[1:], `:\`) { + return (s[0] >= 'a' && s[0] <= 'z') || (s[0] >= 'A' && s[0] <= 'Z') + } + return false +} + +func absolute(input string) (*urlFrag, error) { + u, frag, err := splitFragment(input) + if err != nil { + return nil, err + } + + // if windows absolute file path, convert to file url + // because: net/url parses driver name as scheme + if runtime.GOOS == "windows" && startsWithWindowsDrive(u) { + u = "file:///" + filepath.ToSlash(u) + } + + gourl, err := gourl.Parse(u) + if err != nil { + return nil, &ParseURLError{URL: input, Err: err} + } + if gourl.IsAbs() { + return &urlFrag{url(u), frag}, nil + } + + // avoid filesystem api in wasm + if runtime.GOOS != "js" { + abs, err := filepath.Abs(u) + if err != nil { + return nil, &ParseURLError{URL: input, Err: err} + } + u = abs + } + if !strings.HasPrefix(u, "/") { + u = "/" + u + } + u = "file://" + filepath.ToSlash(u) + + _, err = gourl.Parse(u) + if err != nil { + return nil, &ParseURLError{URL: input, Err: err} + } + return &urlFrag{url: url(u), frag: frag}, nil +} + +func (uf *urlFrag) String() string { + return fmt.Sprintf("%s#%s", uf.url, encode(string(uf.frag))) +} + +// -- + +type urlPtr struct { + url url + ptr jsonPointer +} + +func (up *urlPtr) lookup(v any) (any, error) { + for _, tok := range strings.Split(string(up.ptr), "/")[1:] { + tok, ok := unescape(tok) + if !ok { + return nil, &InvalidJsonPointerError{up.String()} + } + switch val := v.(type) { + case map[string]any: + if pvalue, ok := val[tok]; ok { + v = pvalue + continue + } + case []any: + if index, err := strconv.Atoi(tok); err == nil { + if index >= 0 && index < len(val) { + v = val[index] + continue + } + } + } + return nil, &JSONPointerNotFoundError{up.String()} + } + return v, nil +} + +func (up *urlPtr) format(tok string) string { + return fmt.Sprintf("%s#%s/%s", up.url, encode(string(up.ptr)), encode(escape(tok))) +} + +func (up *urlPtr) String() string { + return fmt.Sprintf("%s#%s", up.url, encode(string(up.ptr))) +} + +// -- + +func minInt(i, j int) int { + if i < j { + return i + } + return j +} + +func strVal(obj map[string]any, prop string) (string, bool) { + v, ok := obj[prop] + if !ok { + return "", false + } + s, ok := v.(string) + return s, ok +} + +func isInteger(num any) bool { + rat, ok := new(big.Rat).SetString(fmt.Sprint(num)) + return ok && rat.IsInt() +} + +// quote returns single-quoted string. +// used for embedding quoted strings in json. +func quote(s string) string { + s = fmt.Sprintf("%q", s) + s = strings.ReplaceAll(s, `\"`, `"`) + s = strings.ReplaceAll(s, `'`, `\'`) + return "'" + s[1:len(s)-1] + "'" +} + +func equals(v1, v2 any) (bool, ErrorKind) { + switch v1 := v1.(type) { + case map[string]any: + v2, ok := v2.(map[string]any) + if !ok || len(v1) != len(v2) { + return false, nil + } + for k, val1 := range v1 { + val2, ok := v2[k] + if !ok { + return false, nil + } + if ok, k := equals(val1, val2); !ok || k != nil { + return ok, k + } + } + return true, nil + case []any: + v2, ok := v2.([]any) + if !ok || len(v1) != len(v2) { + return false, nil + } + for i := range v1 { + if ok, k := equals(v1[i], v2[i]); !ok || k != nil { + return ok, k + } + } + return true, nil + case nil: + return v2 == nil, nil + case bool: + v2, ok := v2.(bool) + return ok && v1 == v2, nil + case string: + v2, ok := v2.(string) + return ok && v1 == v2, nil + case json.Number, float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + num1, ok1 := new(big.Rat).SetString(fmt.Sprint(v1)) + num2, ok2 := new(big.Rat).SetString(fmt.Sprint(v2)) + return ok1 && ok2 && num1.Cmp(num2) == 0, nil + default: + return false, &kind.InvalidJsonValue{Value: v1} + } +} + +func duplicates(arr []any) (int, int, ErrorKind) { + if len(arr) <= 20 { + for i := 1; i < len(arr); i++ { + for j := 0; j < i; j++ { + if ok, k := equals(arr[i], arr[j]); ok || k != nil { + return j, i, k + } + } + } + return -1, -1, nil + } + + m := make(map[uint64][]int) + h := new(maphash.Hash) + for i, item := range arr { + h.Reset() + writeHash(item, h) + hash := h.Sum64() + indexes, ok := m[hash] + if ok { + for _, j := range indexes { + if ok, k := equals(item, arr[j]); ok || k != nil { + return j, i, k + } + } + } + indexes = append(indexes, i) + m[hash] = indexes + } + return -1, -1, nil +} + +func writeHash(v any, h *maphash.Hash) ErrorKind { + switch v := v.(type) { + case map[string]any: + _ = h.WriteByte(0) + props := make([]string, 0, len(v)) + for prop := range v { + props = append(props, prop) + } + slices.Sort(props) + for _, prop := range props { + writeHash(prop, h) + writeHash(v[prop], h) + } + case []any: + _ = h.WriteByte(1) + for _, item := range v { + writeHash(item, h) + } + case nil: + _ = h.WriteByte(2) + case bool: + _ = h.WriteByte(3) + if v { + _ = h.WriteByte(1) + } else { + _ = h.WriteByte(0) + } + case string: + _ = h.WriteByte(4) + _, _ = h.WriteString(v) + case json.Number, float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + _ = h.WriteByte(5) + num, _ := new(big.Rat).SetString(fmt.Sprint(v)) + _, _ = h.Write(num.Num().Bytes()) + _, _ = h.Write(num.Denom().Bytes()) + default: + return &kind.InvalidJsonValue{Value: v} + } + return nil +} + +// -- + +type ParseURLError struct { + URL string + Err error +} + +func (e *ParseURLError) Error() string { + return fmt.Sprintf("error in parsing %q: %v", e.URL, e.Err) +} + +// -- + +type InvalidJsonPointerError struct { + URL string +} + +func (e *InvalidJsonPointerError) Error() string { + return fmt.Sprintf("invalid json-pointer %q", e.URL) +} + +// -- + +type JSONPointerNotFoundError struct { + URL string +} + +func (e *JSONPointerNotFoundError) Error() string { + return fmt.Sprintf("json-pointer in %q not found", e.URL) +} + +// -- + +type SchemaValidationError struct { + URL string + Err error +} + +func (e *SchemaValidationError) Error() string { + return fmt.Sprintf("%q is not valid against metaschema: %v", e.URL, e.Err) +} + +// -- + +// LocalizableError is an error whose message is localizable. +func LocalizableError(format string, args ...any) error { + return &localizableError{format, args} +} + +type localizableError struct { + msg string + args []any +} + +func (e *localizableError) Error() string { + return fmt.Sprintf(e.msg, e.args...) +} + +func (e *localizableError) LocalizedError(p *message.Printer) string { + return p.Sprintf(e.msg, e.args...) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/validator.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/validator.go new file mode 100644 index 000000000..e2ace37a9 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/validator.go @@ -0,0 +1,975 @@ +package jsonschema + +import ( + "encoding/json" + "fmt" + "math/big" + "slices" + "strconv" + "unicode/utf8" + + "github.com/santhosh-tekuri/jsonschema/v6/kind" + "golang.org/x/text/message" +) + +func (sch *Schema) Validate(v any) error { + return sch.validate(v, nil, nil, nil, false, nil) +} + +func (sch *Schema) validate(v any, regexpEngine RegexpEngine, meta *Schema, resources map[jsonPointer]*resource, assertVocabs bool, vocabularies map[string]*Vocabulary) error { + vd := validator{ + v: v, + vloc: make([]string, 0, 8), + sch: sch, + scp: &scope{sch, "", 0, nil}, + uneval: unevalFrom(v, sch, false), + errors: nil, + boolResult: false, + regexpEngine: regexpEngine, + meta: meta, + resources: resources, + assertVocabs: assertVocabs, + vocabularies: vocabularies, + } + if _, err := vd.validate(); err != nil { + verr := err.(*ValidationError) + var causes []*ValidationError + if _, ok := verr.ErrorKind.(*kind.Group); ok { + causes = verr.Causes + } else { + causes = []*ValidationError{verr} + } + return &ValidationError{ + SchemaURL: sch.Location, + InstanceLocation: nil, + ErrorKind: &kind.Schema{Location: sch.Location}, + Causes: causes, + } + } + + return nil +} + +type validator struct { + v any + vloc []string + sch *Schema + scp *scope + uneval *uneval + errors []*ValidationError + boolResult bool // is interested to know valid or not (but not actuall error) + regexpEngine RegexpEngine + + // meta validation + meta *Schema // set only when validating with metaschema + resources map[jsonPointer]*resource // resources which should be validated with their dialect + assertVocabs bool + vocabularies map[string]*Vocabulary +} + +func (vd *validator) validate() (*uneval, error) { + s := vd.sch + v := vd.v + + // boolean -- + if s.Bool != nil { + if *s.Bool { + return vd.uneval, nil + } else { + return nil, vd.error(&kind.FalseSchema{}) + } + } + + // check cycle -- + if scp := vd.scp.checkCycle(); scp != nil { + return nil, vd.error(&kind.RefCycle{ + URL: s.Location, + KeywordLocation1: vd.scp.kwLoc(), + KeywordLocation2: scp.kwLoc(), + }) + } + + t := typeOf(v) + if t == invalidType { + return nil, vd.error(&kind.InvalidJsonValue{Value: v}) + } + + // type -- + if s.Types != nil && !s.Types.IsEmpty() { + matched := s.Types.contains(t) || (s.Types.contains(integerType) && t == numberType && isInteger(v)) + if !matched { + return nil, vd.error(&kind.Type{Got: t.String(), Want: s.Types.ToStrings()}) + } + } + + // const -- + if s.Const != nil { + ok, k := equals(v, *s.Const) + if k != nil { + return nil, vd.error(k) + } else if !ok { + return nil, vd.error(&kind.Const{Got: v, Want: *s.Const}) + } + } + + // enum -- + if s.Enum != nil { + matched := s.Enum.types.contains(typeOf(v)) + if matched { + matched = false + for _, item := range s.Enum.Values { + ok, k := equals(v, item) + if k != nil { + return nil, vd.error(k) + } else if ok { + matched = true + break + } + } + } + if !matched { + return nil, vd.error(&kind.Enum{Got: v, Want: s.Enum.Values}) + } + } + + // format -- + if s.Format != nil { + var err error + if s.Format.Name == "regex" && vd.regexpEngine != nil { + err = vd.regexpEngine.validate(v) + } else { + err = s.Format.Validate(v) + } + if err != nil { + return nil, vd.error(&kind.Format{Got: v, Want: s.Format.Name, Err: err}) + } + } + + // $ref -- + if s.Ref != nil { + err := vd.validateRef(s.Ref, "$ref") + if s.DraftVersion < 2019 { + return vd.uneval, err + } + if err != nil { + vd.addErr(err) + } + } + + // type specific validations -- + switch v := v.(type) { + case map[string]any: + vd.objValidate(v) + case []any: + vd.arrValidate(v) + case string: + vd.strValidate(v) + case json.Number, float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + vd.numValidate(v) + } + + if len(vd.errors) == 0 || !vd.boolResult { + if s.DraftVersion >= 2019 { + vd.validateRefs() + } + vd.condValidate() + + for _, ext := range s.Extensions { + ext.Validate(&ValidatorContext{vd}, v) + } + + if s.DraftVersion >= 2019 { + vd.unevalValidate() + } + } + + switch len(vd.errors) { + case 0: + return vd.uneval, nil + case 1: + return nil, vd.errors[0] + default: + verr := vd.error(&kind.Group{}) + verr.Causes = vd.errors + return nil, verr + } +} + +func (vd *validator) objValidate(obj map[string]any) { + s := vd.sch + + // minProperties -- + if s.MinProperties != nil { + if len(obj) < *s.MinProperties { + vd.addError(&kind.MinProperties{Got: len(obj), Want: *s.MinProperties}) + } + } + + // maxProperties -- + if s.MaxProperties != nil { + if len(obj) > *s.MaxProperties { + vd.addError(&kind.MaxProperties{Got: len(obj), Want: *s.MaxProperties}) + } + } + + // required -- + if len(s.Required) > 0 { + if missing := vd.findMissing(obj, s.Required); missing != nil { + vd.addError(&kind.Required{Missing: missing}) + } + } + + if vd.boolResult && len(vd.errors) > 0 { + return + } + + // dependencies -- + for pname, dep := range s.Dependencies { + if _, ok := obj[pname]; ok { + switch dep := dep.(type) { + case []string: + if missing := vd.findMissing(obj, dep); missing != nil { + vd.addError(&kind.Dependency{Prop: pname, Missing: missing}) + } + case *Schema: + vd.addErr(vd.validateSelf(dep, "", false)) + } + } + } + + var additionalPros []string + for pname, pvalue := range obj { + if vd.boolResult && len(vd.errors) > 0 { + return + } + evaluated := false + + // properties -- + if sch, ok := s.Properties[pname]; ok { + evaluated = true + vd.addErr(vd.validateVal(sch, pvalue, pname)) + } + + // patternProperties -- + for regex, sch := range s.PatternProperties { + if regex.MatchString(pname) { + evaluated = true + vd.addErr(vd.validateVal(sch, pvalue, pname)) + } + } + + if !evaluated && s.AdditionalProperties != nil { + evaluated = true + switch additional := s.AdditionalProperties.(type) { + case bool: + if !additional { + additionalPros = append(additionalPros, pname) + } + case *Schema: + vd.addErr(vd.validateVal(additional, pvalue, pname)) + } + } + + if evaluated { + delete(vd.uneval.props, pname) + } + } + if len(additionalPros) > 0 { + vd.addError(&kind.AdditionalProperties{Properties: additionalPros}) + } + + if s.DraftVersion == 4 { + return + } + + // propertyNames -- + if s.PropertyNames != nil { + for pname := range obj { + sch, meta, resources := s.PropertyNames, vd.meta, vd.resources + res := vd.metaResource(sch) + if res != nil { + meta = res.dialect.getSchema(vd.assertVocabs, vd.vocabularies) + sch = meta + } + if err := sch.validate(pname, vd.regexpEngine, meta, resources, vd.assertVocabs, vd.vocabularies); err != nil { + verr := err.(*ValidationError) + verr.SchemaURL = s.PropertyNames.Location + verr.ErrorKind = &kind.PropertyNames{Property: pname} + vd.addErr(verr) + } + } + } + + if s.DraftVersion == 6 { + return + } + + // dependentSchemas -- + for pname, sch := range s.DependentSchemas { + if _, ok := obj[pname]; ok { + vd.addErr(vd.validateSelf(sch, "", false)) + } + } + + // dependentRequired -- + for pname, reqd := range s.DependentRequired { + if _, ok := obj[pname]; ok { + if missing := vd.findMissing(obj, reqd); missing != nil { + vd.addError(&kind.DependentRequired{Prop: pname, Missing: missing}) + } + } + } +} + +func (vd *validator) arrValidate(arr []any) { + s := vd.sch + + // minItems -- + if s.MinItems != nil { + if len(arr) < *s.MinItems { + vd.addError(&kind.MinItems{Got: len(arr), Want: *s.MinItems}) + } + } + + // maxItems -- + if s.MaxItems != nil { + if len(arr) > *s.MaxItems { + vd.addError(&kind.MaxItems{Got: len(arr), Want: *s.MaxItems}) + } + } + + // uniqueItems -- + if s.UniqueItems && len(arr) > 1 { + i, j, k := duplicates(arr) + if k != nil { + vd.addError(k) + } else if i != -1 { + vd.addError(&kind.UniqueItems{Duplicates: [2]int{i, j}}) + } + } + + if s.DraftVersion < 2020 { + evaluated := 0 + + // items -- + switch items := s.Items.(type) { + case *Schema: + for i, item := range arr { + vd.addErr(vd.validateVal(items, item, strconv.Itoa(i))) + } + evaluated = len(arr) + case []*Schema: + min := minInt(len(arr), len(items)) + for i, item := range arr[:min] { + vd.addErr(vd.validateVal(items[i], item, strconv.Itoa(i))) + } + evaluated = min + } + + // additionalItems -- + if s.AdditionalItems != nil { + switch additional := s.AdditionalItems.(type) { + case bool: + if !additional && evaluated != len(arr) { + vd.addError(&kind.AdditionalItems{Count: len(arr) - evaluated}) + } + case *Schema: + for i, item := range arr[evaluated:] { + vd.addErr(vd.validateVal(additional, item, strconv.Itoa(i))) + } + } + } + } else { + evaluated := minInt(len(s.PrefixItems), len(arr)) + + // prefixItems -- + for i, item := range arr[:evaluated] { + vd.addErr(vd.validateVal(s.PrefixItems[i], item, strconv.Itoa(i))) + } + + // items2020 -- + if s.Items2020 != nil { + for i, item := range arr[evaluated:] { + vd.addErr(vd.validateVal(s.Items2020, item, strconv.Itoa(i))) + } + } + } + + // contains -- + if s.Contains != nil { + var errors []*ValidationError + var matched []int + + for i, item := range arr { + if err := vd.validateVal(s.Contains, item, strconv.Itoa(i)); err != nil { + errors = append(errors, err.(*ValidationError)) + } else { + matched = append(matched, i) + if s.DraftVersion >= 2020 { + delete(vd.uneval.items, i) + } + } + } + + // minContains -- + if s.MinContains != nil { + if len(matched) < *s.MinContains { + vd.addErrors(errors, &kind.MinContains{Got: matched, Want: *s.MinContains}) + } + } else if len(matched) == 0 { + vd.addErrors(errors, &kind.Contains{}) + } + + // maxContains -- + if s.MaxContains != nil { + if len(matched) > *s.MaxContains { + vd.addError(&kind.MaxContains{Got: matched, Want: *s.MaxContains}) + } + } + } +} + +func (vd *validator) strValidate(str string) { + s := vd.sch + + strLen := -1 + if s.MinLength != nil || s.MaxLength != nil { + strLen = utf8.RuneCount([]byte(str)) + } + + // minLength -- + if s.MinLength != nil { + if strLen < *s.MinLength { + vd.addError(&kind.MinLength{Got: strLen, Want: *s.MinLength}) + } + } + + // maxLength -- + if s.MaxLength != nil { + if strLen > *s.MaxLength { + vd.addError(&kind.MaxLength{Got: strLen, Want: *s.MaxLength}) + } + } + + // pattern -- + if s.Pattern != nil { + if !s.Pattern.MatchString(str) { + vd.addError(&kind.Pattern{Got: str, Want: s.Pattern.String()}) + } + } + + if s.DraftVersion == 6 { + return + } + + var err error + + // contentEncoding -- + decoded := []byte(str) + if s.ContentEncoding != nil { + decoded, err = s.ContentEncoding.Decode(str) + if err != nil { + decoded = nil + vd.addError(&kind.ContentEncoding{Want: s.ContentEncoding.Name, Err: err}) + } + } + + var deserialized *any + if decoded != nil && s.ContentMediaType != nil { + if s.ContentSchema == nil { + err = s.ContentMediaType.Validate(decoded) + } else { + var value any + value, err = s.ContentMediaType.UnmarshalJSON(decoded) + if err == nil { + deserialized = &value + } + } + if err != nil { + vd.addError(&kind.ContentMediaType{ + Got: decoded, + Want: s.ContentMediaType.Name, + Err: err, + }) + } + } + + if deserialized != nil && s.ContentSchema != nil { + sch, meta, resources := s.ContentSchema, vd.meta, vd.resources + res := vd.metaResource(sch) + if res != nil { + meta = res.dialect.getSchema(vd.assertVocabs, vd.vocabularies) + sch = meta + } + if err = sch.validate(*deserialized, vd.regexpEngine, meta, resources, vd.assertVocabs, vd.vocabularies); err != nil { + verr := err.(*ValidationError) + verr.SchemaURL = s.Location + verr.ErrorKind = &kind.ContentSchema{} + vd.addErr(verr) + } + } +} + +func (vd *validator) numValidate(v any) { + s := vd.sch + + var numVal *big.Rat + num := func() *big.Rat { + if numVal == nil { + numVal, _ = new(big.Rat).SetString(fmt.Sprintf("%v", v)) + } + return numVal + } + + // minimum -- + if s.Minimum != nil && num().Cmp(s.Minimum) < 0 { + vd.addError(&kind.Minimum{Got: num(), Want: s.Minimum}) + } + + // maximum -- + if s.Maximum != nil && num().Cmp(s.Maximum) > 0 { + vd.addError(&kind.Maximum{Got: num(), Want: s.Maximum}) + } + + // exclusiveMinimum + if s.ExclusiveMinimum != nil && num().Cmp(s.ExclusiveMinimum) <= 0 { + vd.addError(&kind.ExclusiveMinimum{Got: num(), Want: s.ExclusiveMinimum}) + } + + // exclusiveMaximum + if s.ExclusiveMaximum != nil && num().Cmp(s.ExclusiveMaximum) >= 0 { + vd.addError(&kind.ExclusiveMaximum{Got: num(), Want: s.ExclusiveMaximum}) + } + + // multipleOf + if s.MultipleOf != nil { + if q := new(big.Rat).Quo(num(), s.MultipleOf); !q.IsInt() { + vd.addError(&kind.MultipleOf{Got: num(), Want: s.MultipleOf}) + } + } +} + +func (vd *validator) condValidate() { + s := vd.sch + + // not -- + if s.Not != nil { + if vd.validateSelf(s.Not, "", true) == nil { + vd.addError(&kind.Not{}) + } + } + + // allOf -- + if len(s.AllOf) > 0 { + var errors []*ValidationError + for _, sch := range s.AllOf { + if err := vd.validateSelf(sch, "", false); err != nil { + errors = append(errors, err.(*ValidationError)) + if vd.boolResult { + break + } + } + } + if len(errors) != 0 { + vd.addErrors(errors, &kind.AllOf{}) + } + } + + // anyOf + if len(s.AnyOf) > 0 { + var matched bool + var errors []*ValidationError + for _, sch := range s.AnyOf { + if err := vd.validateSelf(sch, "", false); err != nil { + errors = append(errors, err.(*ValidationError)) + } else { + matched = true + // for uneval, all schemas must be evaluated + if vd.uneval.isEmpty() { + break + } + } + } + if !matched { + vd.addErrors(errors, &kind.AnyOf{}) + } + } + + // oneOf + if len(s.OneOf) > 0 { + var matched = -1 + var errors []*ValidationError + for i, sch := range s.OneOf { + if err := vd.validateSelf(sch, "", matched != -1); err != nil { + if matched == -1 { + errors = append(errors, err.(*ValidationError)) + } + } else { + if matched == -1 { + matched = i + } else { + vd.addError(&kind.OneOf{Subschemas: []int{matched, i}}) + break + } + } + } + if matched == -1 { + vd.addErrors(errors, &kind.OneOf{Subschemas: nil}) + } + } + + // if, then, else -- + if s.If != nil { + if vd.validateSelf(s.If, "", true) == nil { + if s.Then != nil { + vd.addErr(vd.validateSelf(s.Then, "", false)) + } + } else if s.Else != nil { + vd.addErr(vd.validateSelf(s.Else, "", false)) + } + } +} + +func (vd *validator) unevalValidate() { + s := vd.sch + + // unevaluatedProperties + if obj, ok := vd.v.(map[string]any); ok && s.UnevaluatedProperties != nil { + for pname := range vd.uneval.props { + if pvalue, ok := obj[pname]; ok { + vd.addErr(vd.validateVal(s.UnevaluatedProperties, pvalue, pname)) + } + } + vd.uneval.props = nil + } + + // unevaluatedItems + if arr, ok := vd.v.([]any); ok && s.UnevaluatedItems != nil { + for i := range vd.uneval.items { + vd.addErr(vd.validateVal(s.UnevaluatedItems, arr[i], strconv.Itoa(i))) + } + vd.uneval.items = nil + } +} + +// validation helpers -- + +func (vd *validator) validateSelf(sch *Schema, refKw string, boolResult bool) error { + scp := vd.scp.child(sch, refKw, vd.scp.vid) + uneval := unevalFrom(vd.v, sch, !vd.uneval.isEmpty()) + subvd := validator{ + v: vd.v, + vloc: vd.vloc, + sch: sch, + scp: scp, + uneval: uneval, + errors: nil, + boolResult: vd.boolResult || boolResult, + regexpEngine: vd.regexpEngine, + meta: vd.meta, + resources: vd.resources, + assertVocabs: vd.assertVocabs, + vocabularies: vd.vocabularies, + } + subvd.handleMeta() + uneval, err := subvd.validate() + if err == nil { + vd.uneval.merge(uneval) + } + return err +} + +func (vd *validator) validateVal(sch *Schema, v any, vtok string) error { + vloc := append(vd.vloc, vtok) + scp := vd.scp.child(sch, "", vd.scp.vid+1) + uneval := unevalFrom(v, sch, false) + subvd := validator{ + v: v, + vloc: vloc, + sch: sch, + scp: scp, + uneval: uneval, + errors: nil, + boolResult: vd.boolResult, + regexpEngine: vd.regexpEngine, + meta: vd.meta, + resources: vd.resources, + assertVocabs: vd.assertVocabs, + vocabularies: vd.vocabularies, + } + subvd.handleMeta() + _, err := subvd.validate() + return err +} + +func (vd *validator) validateValue(sch *Schema, v any, vpath []string) error { + vloc := append(vd.vloc, vpath...) + scp := vd.scp.child(sch, "", vd.scp.vid+1) + uneval := unevalFrom(v, sch, false) + subvd := validator{ + v: v, + vloc: vloc, + sch: sch, + scp: scp, + uneval: uneval, + errors: nil, + boolResult: vd.boolResult, + regexpEngine: vd.regexpEngine, + meta: vd.meta, + resources: vd.resources, + assertVocabs: vd.assertVocabs, + vocabularies: vd.vocabularies, + } + subvd.handleMeta() + _, err := subvd.validate() + return err +} + +func (vd *validator) metaResource(sch *Schema) *resource { + if sch != vd.meta { + return nil + } + ptr := "" + for _, tok := range vd.instanceLocation() { + ptr += "/" + ptr += escape(tok) + } + return vd.resources[jsonPointer(ptr)] +} + +func (vd *validator) handleMeta() { + res := vd.metaResource(vd.sch) + if res == nil { + return + } + sch := res.dialect.getSchema(vd.assertVocabs, vd.vocabularies) + vd.meta = sch + vd.sch = sch +} + +// reference validation -- + +func (vd *validator) validateRef(sch *Schema, kw string) error { + err := vd.validateSelf(sch, kw, false) + if err != nil { + refErr := vd.error(&kind.Reference{Keyword: kw, URL: sch.Location}) + verr := err.(*ValidationError) + if _, ok := verr.ErrorKind.(*kind.Group); ok { + refErr.Causes = verr.Causes + } else { + refErr.Causes = append(refErr.Causes, verr) + } + return refErr + } + return nil +} + +func (vd *validator) resolveRecursiveAnchor(fallback *Schema) *Schema { + sch := fallback + scp := vd.scp + for scp != nil { + if scp.sch.resource.RecursiveAnchor { + sch = scp.sch + } + scp = scp.parent + } + return sch +} + +func (vd *validator) resolveDynamicAnchor(name string, fallback *Schema) *Schema { + sch := fallback + scp := vd.scp + for scp != nil { + if dsch, ok := scp.sch.resource.dynamicAnchors[name]; ok { + sch = dsch + } + scp = scp.parent + } + return sch +} + +func (vd *validator) validateRefs() { + // $recursiveRef -- + if sch := vd.sch.RecursiveRef; sch != nil { + if sch.RecursiveAnchor { + sch = vd.resolveRecursiveAnchor(sch) + } + vd.addErr(vd.validateRef(sch, "$recursiveRef")) + } + + // $dynamicRef -- + if dref := vd.sch.DynamicRef; dref != nil { + sch := dref.Ref // initial target + if dref.Anchor != "" { + // $dynamicRef includes anchor + if sch.DynamicAnchor == dref.Anchor { + // initial target has matching $dynamicAnchor + sch = vd.resolveDynamicAnchor(dref.Anchor, sch) + } + } + vd.addErr(vd.validateRef(sch, "$dynamicRef")) + } +} + +// error helpers -- + +func (vd *validator) instanceLocation() []string { + return slices.Clone(vd.vloc) +} + +func (vd *validator) error(kind ErrorKind) *ValidationError { + if vd.boolResult { + return &ValidationError{} + } + return &ValidationError{ + SchemaURL: vd.sch.Location, + InstanceLocation: vd.instanceLocation(), + ErrorKind: kind, + Causes: nil, + } +} + +func (vd *validator) addErr(err error) { + if err != nil { + vd.errors = append(vd.errors, err.(*ValidationError)) + } +} + +func (vd *validator) addError(kind ErrorKind) { + vd.errors = append(vd.errors, vd.error(kind)) +} + +func (vd *validator) addErrors(errors []*ValidationError, kind ErrorKind) { + err := vd.error(kind) + err.Causes = errors + vd.errors = append(vd.errors, err) +} + +func (vd *validator) findMissing(obj map[string]any, reqd []string) []string { + var missing []string + for _, pname := range reqd { + if _, ok := obj[pname]; !ok { + if vd.boolResult { + return []string{} // non-nil + } + missing = append(missing, pname) + } + } + return missing +} + +// -- + +type scope struct { + sch *Schema + + // if empty, compute from self.sch and self.parent.sch. + // not empty, only when there is a jump i.e, $ref, $XXXRef + refKeyword string + + // unique id of value being validated + // if two scopes validate same value, they will have + // same vid + vid int + + parent *scope +} + +func (sc *scope) child(sch *Schema, refKeyword string, vid int) *scope { + return &scope{sch, refKeyword, vid, sc} +} + +func (sc *scope) checkCycle() *scope { + scp := sc.parent + for scp != nil { + if scp.vid != sc.vid { + break + } + if scp.sch == sc.sch { + return scp + } + scp = scp.parent + } + return nil +} + +func (sc *scope) kwLoc() string { + var loc string + for sc.parent != nil { + if sc.refKeyword != "" { + loc = fmt.Sprintf("/%s%s", escape(sc.refKeyword), loc) + } else { + cur := sc.sch.Location + parent := sc.parent.sch.Location + loc = fmt.Sprintf("%s%s", cur[len(parent):], loc) + } + sc = sc.parent + } + return loc +} + +// -- + +type uneval struct { + props map[string]struct{} + items map[int]struct{} +} + +func unevalFrom(v any, sch *Schema, callerNeeds bool) *uneval { + uneval := &uneval{} + switch v := v.(type) { + case map[string]any: + if !sch.allPropsEvaluated && (callerNeeds || sch.UnevaluatedProperties != nil) { + uneval.props = map[string]struct{}{} + for k := range v { + uneval.props[k] = struct{}{} + } + } + case []any: + if !sch.allItemsEvaluated && (callerNeeds || sch.UnevaluatedItems != nil) && sch.numItemsEvaluated < len(v) { + uneval.items = map[int]struct{}{} + for i := sch.numItemsEvaluated; i < len(v); i++ { + uneval.items[i] = struct{}{} + } + } + } + return uneval +} + +func (ue *uneval) merge(other *uneval) { + for k := range ue.props { + if _, ok := other.props[k]; !ok { + delete(ue.props, k) + } + } + for i := range ue.items { + if _, ok := other.items[i]; !ok { + delete(ue.items, i) + } + } +} + +func (ue *uneval) isEmpty() bool { + return len(ue.props) == 0 && len(ue.items) == 0 +} + +// -- + +type ValidationError struct { + // absolute, dereferenced schema location. + SchemaURL string + + // location of the JSON value within the instance being validated. + InstanceLocation []string + + // kind of error + ErrorKind ErrorKind + + // holds nested errors + Causes []*ValidationError +} + +type ErrorKind interface { + KeywordPath() []string + LocalizedString(*message.Printer) string +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/vocab.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/vocab.go new file mode 100644 index 000000000..c81cb700f --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/vocab.go @@ -0,0 +1,111 @@ +package jsonschema + +// CompilerContext provides helpers for +// compiling a [Vocabulary]. +type CompilerContext struct { + c *objCompiler +} + +func (ctx *CompilerContext) Enqueue(schPath []string) *Schema { + ptr := ctx.c.up.ptr + for _, tok := range schPath { + ptr = ptr.append(tok) + } + return ctx.c.enqueuePtr(ptr) +} + +// Vocabulary defines a set of keywords, their syntax and +// their semantics. +type Vocabulary struct { + // URL identifier for this Vocabulary. + URL string + + // Schema that is used to validate the keywords that is introduced by this + // vocabulary. + Schema *Schema + + // Subschemas lists the possible locations of subschemas introduced by + // this vocabulary. + Subschemas []SchemaPath + + // Compile compiles the keywords(introduced by this vocabulary) in obj into [SchemaExt]. + // If obj does not contain any keywords introduced by this vocabulary, nil SchemaExt must + // be returned. + Compile func(ctx *CompilerContext, obj map[string]any) (SchemaExt, error) +} + +// -- + +// SchemaExt is compled form of vocabulary. +type SchemaExt interface { + // Validate validates v against and errors if any are reported + // to ctx. + Validate(ctx *ValidatorContext, v any) +} + +// ValidatorContext provides helpers for +// validating with [SchemaExt]. +type ValidatorContext struct { + vd *validator +} + +// ValueLocation returns location of value as jsonpath token array. +func (ctx *ValidatorContext) ValueLocation() []string { + return ctx.vd.vloc +} + +// Validate validates v with sch. vpath gives path of v from current context value. +func (ctx *ValidatorContext) Validate(sch *Schema, v any, vpath []string) error { + switch len(vpath) { + case 0: + return ctx.vd.validateSelf(sch, "", false) + case 1: + return ctx.vd.validateVal(sch, v, vpath[0]) + default: + return ctx.vd.validateValue(sch, v, vpath) + } +} + +// EvaluatedProp marks given property of current object as evaluated. +func (ctx *ValidatorContext) EvaluatedProp(pname string) { + delete(ctx.vd.uneval.props, pname) +} + +// EvaluatedItem marks items at given index of current array as evaluated. +func (ctx *ValidatorContext) EvaluatedItem(index int) { + delete(ctx.vd.uneval.items, index) +} + +// AddError reports validation-error of given kind. +func (ctx *ValidatorContext) AddError(k ErrorKind) { + ctx.vd.addError(k) +} + +// AddErrors reports validation-errors of given kind. +func (ctx *ValidatorContext) AddErrors(errors []*ValidationError, k ErrorKind) { + ctx.vd.addErrors(errors, k) +} + +// AddErr reports the given err. This is typically used to report +// the error created by subschema validation. +// +// NOTE that err must be of type *ValidationError. +func (ctx *ValidatorContext) AddErr(err error) { + ctx.vd.addErr(err) +} + +func (ctx *ValidatorContext) Equals(v1, v2 any) (bool, error) { + b, k := equals(v1, v2) + if k != nil { + return false, ctx.vd.error(k) + } + return b, nil +} + +func (ctx *ValidatorContext) Duplicates(arr []any) (int, int, error) { + i, j, k := duplicates(arr) + if k != nil { + return -1, -1, ctx.vd.error(k) + } + return i, j, nil +} diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/LICENSE b/vendor/github.com/speakeasy-api/openapi-overlay/LICENSE deleted file mode 100644 index 467ab518e..000000000 --- a/vendor/github.com/speakeasy-api/openapi-overlay/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -Copyright 2023 SPEAKEASY DEVELOPMENT INC - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/apply.go b/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/apply.go deleted file mode 100644 index faaaf9230..000000000 --- a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/apply.go +++ /dev/null @@ -1,238 +0,0 @@ -package overlay - -import ( - "fmt" - "github.com/speakeasy-api/jsonpath/pkg/jsonpath/config" - "github.com/speakeasy-api/jsonpath/pkg/jsonpath/token" - "gopkg.in/yaml.v3" - "strings" -) - -// ApplyTo will take an overlay and apply its changes to the given YAML -// document. -func (o *Overlay) ApplyTo(root *yaml.Node) error { - for _, action := range o.Actions { - var err error - if action.Remove { - err = o.applyRemoveAction(root, action, nil) - } else { - err = o.applyUpdateAction(root, action, &[]string{}) - } - - if err != nil { - return err - } - } - - return nil -} - -func (o *Overlay) ApplyToStrict(root *yaml.Node) (error, []string) { - multiError := []string{} - warnings := []string{} - hasFilterExpression := false - for i, action := range o.Actions { - tokens := token.NewTokenizer(action.Target, config.WithPropertyNameExtension()).Tokenize() - for _, tok := range tokens { - if tok.Token == token.FILTER { - hasFilterExpression = true - } - } - - actionWarnings := []string{} - err := o.validateSelectorHasAtLeastOneTarget(root, action) - if err != nil { - multiError = append(multiError, err.Error()) - } - if action.Remove { - err = o.applyRemoveAction(root, action, &actionWarnings) - } else { - err = o.applyUpdateAction(root, action, &actionWarnings) - } - for _, warning := range actionWarnings { - warnings = append(warnings, fmt.Sprintf("update action (%v / %v) target=%s: %s", i+1, len(o.Actions), action.Target, warning)) - } - } - - if hasFilterExpression && !o.UsesRFC9535() { - warnings = append(warnings, "overlay has a filter expression but lacks `x-speakeasy-jsonpath: rfc9535` extension. Deprecated jsonpath behaviour in use. See overlay.speakeasy.com for the implementation playground.") - } - - if len(multiError) > 0 { - return fmt.Errorf("error applying overlay (strict): %v", strings.Join(multiError, ",")), warnings - } - return nil, warnings -} - -func (o *Overlay) validateSelectorHasAtLeastOneTarget(root *yaml.Node, action Action) error { - if action.Target == "" { - return nil - } - - p, err := o.NewPath(action.Target, nil) - if err != nil { - return err - } - - nodes := p.Query(root) - - if len(nodes) == 0 { - return fmt.Errorf("selector %q did not match any targets", action.Target) - } - - return nil -} - -func (o *Overlay) applyRemoveAction(root *yaml.Node, action Action, warnings *[]string) error { - if action.Target == "" { - return nil - } - - idx := newParentIndex(root) - - p, err := o.NewPath(action.Target, warnings) - if err != nil { - return err - } - - nodes := p.Query(root) - if err != nil { - return err - } - - for _, node := range nodes { - removeNode(idx, node) - } - - return nil -} - -func removeNode(idx parentIndex, node *yaml.Node) { - parent := idx.getParent(node) - if parent == nil { - return - } - - for i, child := range parent.Content { - if child == node { - switch parent.Kind { - case yaml.MappingNode: - if i%2 == 1 { - // if we select a value, we should delete the key too - parent.Content = append(parent.Content[:i-1], parent.Content[i+1:]...) - } else { - // if we select a key, we should delete the value - parent.Content = append(parent.Content[:i], parent.Content[i+2:]...) - } - return - case yaml.SequenceNode: - parent.Content = append(parent.Content[:i], parent.Content[i+1:]...) - return - } - } - } -} - -func (o *Overlay) applyUpdateAction(root *yaml.Node, action Action, warnings *[]string) error { - if action.Target == "" { - return nil - } - - if action.Update.IsZero() { - return nil - } - - p, err := o.NewPath(action.Target, warnings) - if err != nil { - return err - } - - nodes := p.Query(root) - prior, err := yaml.Marshal(root) - if err != nil { - return err - } - - for _, node := range nodes { - if err := updateNode(node, &action.Update); err != nil { - return err - } - } - post, err := yaml.Marshal(root) - if err != nil { - return err - } - if warnings != nil && string(prior) == string(post) { - *warnings = append(*warnings, "does nothing") - } - - return nil -} - -func updateNode(node *yaml.Node, updateNode *yaml.Node) error { - mergeNode(node, updateNode) - return nil -} - -func mergeNode(node *yaml.Node, merge *yaml.Node) { - if node.Kind != merge.Kind { - *node = *clone(merge) - return - } - switch node.Kind { - default: - node.Value = merge.Value - case yaml.MappingNode: - mergeMappingNode(node, merge) - case yaml.SequenceNode: - mergeSequenceNode(node, merge) - } -} - -// mergeMappingNode will perform a shallow merge of the merge node into the main -// node. -func mergeMappingNode(node *yaml.Node, merge *yaml.Node) { -NextKey: - for i := 0; i < len(merge.Content); i += 2 { - mergeKey := merge.Content[i].Value - mergeValue := merge.Content[i+1] - - for j := 0; j < len(node.Content); j += 2 { - nodeKey := node.Content[j].Value - if nodeKey == mergeKey { - mergeNode(node.Content[j+1], mergeValue) - continue NextKey - } - } - - node.Content = append(node.Content, merge.Content[i], clone(mergeValue)) - } -} - -// mergeSequenceNode will append the merge node's content to the original node. -func mergeSequenceNode(node *yaml.Node, merge *yaml.Node) { - node.Content = append(node.Content, clone(merge).Content...) -} - -func clone(node *yaml.Node) *yaml.Node { - newNode := &yaml.Node{ - Kind: node.Kind, - Style: node.Style, - Tag: node.Tag, - Value: node.Value, - Anchor: node.Anchor, - HeadComment: node.HeadComment, - LineComment: node.LineComment, - FootComment: node.FootComment, - } - if node.Alias != nil { - newNode.Alias = clone(node.Alias) - } - if node.Content != nil { - newNode.Content = make([]*yaml.Node, len(node.Content)) - for i, child := range node.Content { - newNode.Content[i] = clone(child) - } - } - return newNode -} diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/jsonpath.go b/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/jsonpath.go deleted file mode 100644 index d43617c74..000000000 --- a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/jsonpath.go +++ /dev/null @@ -1,47 +0,0 @@ -package overlay - -import ( - "fmt" - "github.com/speakeasy-api/jsonpath/pkg/jsonpath" - "github.com/speakeasy-api/jsonpath/pkg/jsonpath/config" - "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" - "gopkg.in/yaml.v3" -) - -type Queryable interface { - Query(root *yaml.Node) []*yaml.Node -} - -type yamlPathQueryable struct { - path *yamlpath.Path -} - -func (y yamlPathQueryable) Query(root *yaml.Node) []*yaml.Node { - if y.path == nil { - return []*yaml.Node{} - } - // errors aren't actually possible from yamlpath. - result, _ := y.path.Find(root) - return result -} - -func (o *Overlay) NewPath(target string, warnings *[]string) (Queryable, error) { - rfcJSONPath, rfcJSONPathErr := jsonpath.NewPath(target, config.WithPropertyNameExtension()) - if o.UsesRFC9535() { - return rfcJSONPath, rfcJSONPathErr - } - if rfcJSONPathErr != nil && warnings != nil { - *warnings = append(*warnings, fmt.Sprintf("invalid rfc9535 jsonpath %s: %s\nThis will be treated as an error in the future. Please fix and opt into the new implementation with `\"x-speakeasy-jsonpath\": rfc9535` in the root of your overlay. See overlay.speakeasy.com for an implementation playground.", target, rfcJSONPathErr.Error())) - } - - path, err := yamlpath.NewPath(target) - return mustExecute(path), err -} - -func (o *Overlay) UsesRFC9535() bool { - return o.JSONPathVersion == "rfc9535" -} - -func mustExecute(path *yamlpath.Path) yamlPathQueryable { - return yamlPathQueryable{path} -} diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/validate.go b/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/validate.go deleted file mode 100644 index 922592eba..000000000 --- a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/validate.go +++ /dev/null @@ -1,61 +0,0 @@ -package overlay - -import ( - "fmt" - "net/url" - "strings" -) - -type ValidationErrors []error - -func (v ValidationErrors) Error() string { - msgs := make([]string, len(v)) - for i, err := range v { - msgs[i] = err.Error() - } - return strings.Join(msgs, "\n") -} - -func (v ValidationErrors) Return() error { - if len(v) > 0 { - return v - } - return nil -} - -func (o *Overlay) Validate() error { - errs := make(ValidationErrors, 0) - if o.Version != "1.0.0" { - errs = append(errs, fmt.Errorf("overlay version must be 1.0.0")) - } - - if o.Info.Title == "" { - errs = append(errs, fmt.Errorf("overlay info title must be defined")) - } - if o.Info.Version == "" { - errs = append(errs, fmt.Errorf("overlay info version must be defined")) - } - - if o.Extends != "" { - _, err := url.Parse(o.Extends) - if err != nil { - errs = append(errs, fmt.Errorf("overlay extends must be a valid URL")) - } - } - - if len(o.Actions) == 0 { - errs = append(errs, fmt.Errorf("overlay must define at least one action")) - } else { - for i, action := range o.Actions { - if action.Target == "" { - errs = append(errs, fmt.Errorf("overlay action at index %d target must be defined", i)) - } - - if action.Remove && !action.Update.IsZero() { - errs = append(errs, fmt.Errorf("overlay action at index %d should not both set remove and define update", i)) - } - } - } - - return errs.Return() -} diff --git a/vendor/github.com/perimeterx/marshmallow/LICENSE b/vendor/github.com/speakeasy-api/openapi/LICENSE similarity index 97% rename from vendor/github.com/perimeterx/marshmallow/LICENSE rename to vendor/github.com/speakeasy-api/openapi/LICENSE index 8ffe86916..f5ddfbccd 100644 --- a/vendor/github.com/perimeterx/marshmallow/LICENSE +++ b/vendor/github.com/speakeasy-api/openapi/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 PerimeterX +Copyright (c) 2024 Speakeasy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/speakeasy-api/openapi/internal/sliceutil/sliceutil.go b/vendor/github.com/speakeasy-api/openapi/internal/sliceutil/sliceutil.go new file mode 100644 index 000000000..42d806500 --- /dev/null +++ b/vendor/github.com/speakeasy-api/openapi/internal/sliceutil/sliceutil.go @@ -0,0 +1,9 @@ +package sliceutil + +func Map[T any, U any](slice []T, fn func(T) U) []U { + mapped := make([]U, len(slice)) + for i, elem := range slice { + mapped[i] = fn(elem) + } + return mapped +} diff --git a/vendor/github.com/speakeasy-api/openapi/internal/version/version.go b/vendor/github.com/speakeasy-api/openapi/internal/version/version.go new file mode 100644 index 000000000..6054f41c3 --- /dev/null +++ b/vendor/github.com/speakeasy-api/openapi/internal/version/version.go @@ -0,0 +1,125 @@ +package version + +import ( + "fmt" + "strconv" + "strings" +) + +type Version struct { + Major int + Minor int + Patch int +} + +var _ fmt.Stringer = (*Version)(nil) + +func New(major, minor, patch int) *Version { + return &Version{ + Major: major, + Minor: minor, + Patch: patch, + } +} + +func (v Version) String() string { + return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) +} + +func (v Version) Equal(other Version) bool { + return v.Major == other.Major && v.Minor == other.Minor && v.Patch == other.Patch +} + +func (v Version) GreaterThan(other Version) bool { + if v.Major > other.Major { + return true + } else if v.Major < other.Major { + return false + } + + if v.Minor > other.Minor { + return true + } else if v.Minor < other.Minor { + return false + } + + return v.Patch > other.Patch +} + +func (v Version) LessThan(other Version) bool { + return !v.Equal(other) && !v.GreaterThan(other) +} + +func (v Version) IsOneOf(versions []*Version) bool { + for _, ver := range versions { + if ver == nil { + continue + } + if v.Equal(*ver) { + return true + } + } + return false +} + +func Parse(version string) (*Version, error) { + parts := strings.Split(version, ".") + if len(parts) != 3 { + return nil, fmt.Errorf("invalid version %s", version) + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return nil, fmt.Errorf("invalid major version %s: %w", parts[0], err) + } + if major < 0 { + return nil, fmt.Errorf("invalid major version %s: cannot be negative", parts[0]) + } + + minor, err := strconv.Atoi(parts[1]) + if err != nil { + return nil, fmt.Errorf("invalid minor version %s: %w", parts[1], err) + } + if minor < 0 { + return nil, fmt.Errorf("invalid minor version %s: cannot be negative", parts[1]) + } + + patch, err := strconv.Atoi(parts[2]) + if err != nil { + return nil, fmt.Errorf("invalid patch version %s: %w", parts[2], err) + } + if patch < 0 { + return nil, fmt.Errorf("invalid patch version %s: cannot be negative", parts[2]) + } + + return New(major, minor, patch), nil +} + +func MustParse(version string) *Version { + v, err := Parse(version) + if err != nil { + panic(err) + } + return v +} + +func IsGreaterOrEqual(a, b string) (bool, error) { + versionA, err := Parse(a) + if err != nil { + return false, fmt.Errorf("invalid version %s: %w", a, err) + } + + versionB, err := Parse(b) + if err != nil { + return false, fmt.Errorf("invalid version %s: %w", b, err) + } + return versionA.Equal(*versionB) || versionA.GreaterThan(*versionB), nil +} + +func IsLessThan(a, b string) (bool, error) { + greaterOrEqual, err := IsGreaterOrEqual(a, b) + if err != nil { + return false, err + } + return !greaterOrEqual, nil +} diff --git a/vendor/github.com/speakeasy-api/openapi/overlay/README.md b/vendor/github.com/speakeasy-api/openapi/overlay/README.md new file mode 100644 index 000000000..6a005f32b --- /dev/null +++ b/vendor/github.com/speakeasy-api/openapi/overlay/README.md @@ -0,0 +1,297 @@ +

+

+ OpenAPI +

+

OpenAPI Overlay

+

An implementation of the OpenAPI Overlay Specification for applying modifications to OpenAPI documents +

+

+ + Overlay reference + + Built by Speakeasy + Release + Go Doc +
+ GitHub Action: Test + Go Report Card + Software License +
+ +

+

+ +> ⚠️ This an alpha implementation. If you'd like to discuss a production use case please join the Speakeasy [slack](https://join.slack.com/t/speakeasy-dev/shared_invite/zt-1df0lalk5-HCAlpcQiqPw8vGukQWhexw). + +## Features + +- **OpenAPI Overlay Specification Compliance**: Full implementation of the [OpenAPI Overlay Specification](https://github.com/OAI/Overlay-Specification/blob/3f398c6/versions/1.0.0.md) (2023-10-12) and [version 1.1.0](https://github.com/OAI/Overlay-Specification/blob/e2c3cec/versions/1.1.0-dev.md) +- **JSONPath Target Selection**: Uses JSONPath expressions to select nodes for modification +- **Remove, Update, and Copy Actions**: Support for remove actions (pruning nodes), update actions (merging values), and copy actions (duplicating or moving nodes) +- **Flexible Input/Output**: Works with both YAML and JSON formats +- **Batch Operations**: Apply multiple modifications to large numbers of nodes in a single operation +- **YAML v1.2 Support**: Uses [gopkg.in/yaml.v3](https://pkg.go.dev/gopkg.in/yaml.v3) for YAML v1.2 parsing (superset of JSON) + +## About OpenAPI Overlays + +This specification defines a means of editing an OpenAPI Specification file by applying a list of actions. Each action is either a remove action that prunes nodes or an update that merges a value into nodes. The nodes impacted are selected by a target expression which uses JSONPath. This implementation also supports [version 1.1.0](https://github.com/OAI/Overlay-Specification/blob/e2c3cec/versions/1.1.0-dev.md) which adds a `copy` action for duplicating or moving nodes within the document. + +The specification itself says very little about the input file to be modified or the output file. The presumed intention is that the input and output be an OpenAPI Specification, but that is not required. + +In many ways, this is similar to [JSONPatch](https://jsonpatch.com/), but without the requirement to use a single explicit path for each operation. This allows the creator of an overlay file to apply a single modification to a large number of nodes in the file within a single operation. + + + +## Apply an overlay to an OpenAPI document + +Shows loading an overlay specification and applying it to transform an OpenAPI document. + +```go +overlayContent := `overlay: 1.0.0 +info: + title: Pet Store Enhancement Overlay + version: 1.0.0 +actions: + - target: $.info.description + update: Enhanced pet store API with additional features` + +openAPIContent := `openapi: 3.1.0 +info: + title: Pet Store API + version: 1.0.0 + description: A simple pet store API +paths: + /pets: + get: + summary: List pets + responses: + '200': + description: A list of pets` + +overlayFile := "temp_overlay.yaml" +openAPIFile := "temp_openapi.yaml" +if err := os.WriteFile(overlayFile, []byte(overlayContent), 0644); err != nil { + panic(err) +} +if err := os.WriteFile(openAPIFile, []byte(openAPIContent), 0644); err != nil { + panic(err) +} +defer os.Remove(overlayFile) +defer os.Remove(openAPIFile) + +overlayDoc, err := overlay.Parse(overlayFile) +if err != nil { + panic(err) +} + +openAPINode, err := loader.LoadSpecification(openAPIFile) +if err != nil { + panic(err) +} + +err = overlayDoc.ApplyTo(openAPINode) +if err != nil { + panic(err) +} + +// Convert back to YAML string +var buf strings.Builder +encoder := yaml.NewEncoder(&buf) +encoder.SetIndent(2) +err = encoder.Encode(openAPINode) +if err != nil { + panic(err) +} + +fmt.Printf("Transformed document:\n%s", buf.String()) +``` + +## Create an overlay specification programmatically + +Shows building an overlay specification with update and remove actions. + +```go +// Create update value as yaml.Node +var updateNode yaml.Node +updateNode.SetString("Enhanced API with additional features") + +overlayDoc := &overlay.Overlay{ + Version: "1.0.0", + Info: overlay.Info{ + Title: "API Enhancement Overlay", + Version: "1.0.0", + }, + Actions: []overlay.Action{ + { + Target: "$.info.description", + Update: updateNode, + }, + { + Target: "$.paths['/deprecated-endpoint']", + Remove: true, + }, + }, +} + +result, err := overlayDoc.ToString() +if err != nil { + panic(err) +} + +fmt.Printf("Overlay specification:\n%s", result) +``` + +## Parse an overlay specification from a file + +Shows loading an overlay file and accessing its properties. + +```go +overlayContent := `overlay: 1.0.0 +info: + title: API Modification Overlay + version: 1.0.0 +actions: + - target: $.info.title + update: Enhanced Pet Store API + - target: $.info.version + update: 2.0.0` + +overlayFile := "temp_overlay.yaml" +if err := os.WriteFile(overlayFile, []byte(overlayContent), 0644); err != nil { + panic(err) +} +defer func() { _ = os.Remove(overlayFile) }() + +overlayDoc, err := overlay.Parse(overlayFile) +if err != nil { + panic(err) +} + +fmt.Printf("Overlay Version: %s\n", overlayDoc.Version) +fmt.Printf("Title: %s\n", overlayDoc.Info.Title) +fmt.Printf("Number of Actions: %d\n", len(overlayDoc.Actions)) + +for i, action := range overlayDoc.Actions { + fmt.Printf("Action %d Target: %s\n", i+1, action.Target) +} +``` + +## Validate an overlay specification + +Shows loading and validating an overlay specification for correctness. + +```go +invalidOverlay := `overlay: 1.0.0 +info: + title: Invalid Overlay +actions: + - target: $.info.title + description: Missing update or remove` + +overlayFile := "temp_invalid_overlay.yaml" +if err := os.WriteFile(overlayFile, []byte(invalidOverlay), 0644); err != nil { + panic(err) +} +defer func() { _ = os.Remove(overlayFile) }() + +overlayDoc, err := overlay.Parse(overlayFile) +if err != nil { + fmt.Printf("Parse error: %s\n", err.Error()) + return +} + +validationErr := overlayDoc.Validate() +if validationErr != nil { + fmt.Println("Validation errors:") + fmt.Printf(" %s\n", validationErr.Error()) +} else { + fmt.Println("Overlay specification is valid!") +} +``` + +## Use remove actions in overlays + +Shows removing specific paths and properties from an OpenAPI document. + +```go +openAPIContent := `openapi: 3.1.0 +info: + title: API + version: 1.0.0 +paths: + /users: + get: + summary: List users + /users/{id}: + get: + summary: Get user + /admin: + get: + summary: Admin endpoint + deprecated: true` + +overlayContent := `overlay: 1.0.0 +info: + title: Cleanup Overlay + version: 1.0.0 +actions: + - target: $.paths['/admin'] + remove: true` + +openAPIFile := "temp_openapi.yaml" +overlayFile := "temp_overlay.yaml" +if err := os.WriteFile(openAPIFile, []byte(openAPIContent), 0644); err != nil { + panic(err) +} +if err := os.WriteFile(overlayFile, []byte(overlayContent), 0644); err != nil { + panic(err) +} +defer func() { _ = os.Remove(openAPIFile) }() +defer func() { _ = os.Remove(overlayFile) }() + +overlayDoc, err := overlay.Parse(overlayFile) +if err != nil { + panic(err) +} + +openAPINode, err := loader.LoadSpecification(openAPIFile) +if err != nil { + panic(err) +} + +err = overlayDoc.ApplyTo(openAPINode) +if err != nil { + panic(err) +} + +var buf strings.Builder +encoder := yaml.NewEncoder(&buf) +encoder.SetIndent(2) +err = encoder.Encode(openAPINode) +if err != nil { + panic(err) +} + +fmt.Printf("Document after removing deprecated endpoint:\n%s", buf.String()) +``` + + + +## Contributing + +This repository is maintained by Speakeasy, but we welcome and encourage contributions from the community to help improve its capabilities and stability. + +### How to Contribute + +1. **Open Issues**: Found a bug or have a feature suggestion? Open an issue to describe what you'd like to see changed. + +2. **Pull Requests**: We welcome pull requests! If you'd like to contribute code: + - Fork the repository + - Create a new branch for your feature/fix + - Submit a PR with a clear description of the changes and any related issues + +3. **Feedback**: Share your experience using the packages or suggest improvements. + +All contributions, whether they're bug reports, feature requests, or code changes, help make this project better for everyone. + +Please ensure your contributions adhere to our coding standards and include appropriate tests where applicable. diff --git a/vendor/github.com/speakeasy-api/openapi/overlay/apply.go b/vendor/github.com/speakeasy-api/openapi/overlay/apply.go new file mode 100644 index 000000000..acaa8cc40 --- /dev/null +++ b/vendor/github.com/speakeasy-api/openapi/overlay/apply.go @@ -0,0 +1,451 @@ +package overlay + +import ( + "fmt" + "strings" + + "github.com/speakeasy-api/jsonpath/pkg/jsonpath/config" + "github.com/speakeasy-api/jsonpath/pkg/jsonpath/token" + "gopkg.in/yaml.v3" +) + +// ApplyTo will take an overlay and apply its changes to the given YAML +// document. +func (o *Overlay) ApplyTo(root *yaml.Node) error { + // Priority is: remove > update > copy + // Copy has no impact if remove is true or update contains a value + for _, action := range o.Actions { + var err error + switch { + case action.Remove: + err = o.applyRemoveAction(root, action, nil) + case !action.Update.IsZero(): + err = o.applyUpdateAction(root, action, &[]string{}, false) + case action.Copy != "": + err = o.applyCopyAction(root, action, &[]string{}, false) + } + + if err != nil { + return err + } + } + + return nil +} + +func (o *Overlay) ApplyToStrict(root *yaml.Node) ([]string, error) { + multiError := []string{} + warnings := []string{} + hasFilterExpression := false + + // Priority is: remove > update > copy + // Copy has no impact if remove is true or update contains a value + for i, action := range o.Actions { + tokens := token.NewTokenizer(action.Target, config.WithPropertyNameExtension()).Tokenize() + for _, tok := range tokens { + if tok.Token == token.FILTER { + hasFilterExpression = true + } + } + + actionWarnings := []string{} + err := o.validateSelectorHasAtLeastOneTarget(root, action) + if err != nil { + multiError = append(multiError, err.Error()) + } + + // Determine action type based on priority: remove > update > copy + actionType := "unknown" + switch { + case action.Remove: + actionType = "remove" + err = o.applyRemoveAction(root, action, &actionWarnings) + case !action.Update.IsZero(): + actionType = "update" + err = o.applyUpdateAction(root, action, &actionWarnings, true) + case action.Copy != "": + actionType = "copy" + err = o.applyCopyAction(root, action, &actionWarnings, true) + default: + err = fmt.Errorf("unknown action type: %v", action) + } + if err != nil { + return nil, err + } + for _, warning := range actionWarnings { + warnings = append(warnings, fmt.Sprintf("%s action (%v / %v) target=%s: %s", actionType, i+1, len(o.Actions), action.Target, warning)) + } + } + + if hasFilterExpression && !o.UsesRFC9535() { + warnings = append(warnings, "overlay has a filter expression but lacks `x-speakeasy-jsonpath: rfc9535` extension. Deprecated jsonpath behaviour in use. See overlay.speakeasy.com for the implementation playground.") + } + + if len(multiError) > 0 { + return warnings, fmt.Errorf("error applying overlay (strict): %v", strings.Join(multiError, ",")) + } + return warnings, nil +} + +func (o *Overlay) validateSelectorHasAtLeastOneTarget(root *yaml.Node, action Action) error { + if action.Target == "" { + return nil + } + + p, err := o.NewPath(action.Target, nil) + if err != nil { + return err + } + + nodes := p.Query(root) + + if len(nodes) == 0 { + return fmt.Errorf("selector %q did not match any targets", action.Target) + } + + // For copy actions, validate the source path (only if copy will actually be applied) + // Copy has no impact if remove is true or update contains a value + if action.Copy != "" && !action.Remove && action.Update.IsZero() { + sourcePath, err := o.NewPath(action.Copy, nil) + if err != nil { + return err + } + + sourceNodes := sourcePath.Query(root) + if len(sourceNodes) == 0 { + return fmt.Errorf("copy source selector %q did not match any nodes", action.Copy) + } + + if len(sourceNodes) > 1 { + return fmt.Errorf("copy source selector %q matched multiple nodes (%d), expected exactly one", action.Copy, len(sourceNodes)) + } + } + + return nil +} + +func (o *Overlay) applyRemoveAction(root *yaml.Node, action Action, warnings *[]string) error { + if action.Target == "" { + return nil + } + + idx := newParentIndex(root) + + p, err := o.NewPath(action.Target, warnings) + if err != nil { + return err + } + + nodes := p.Query(root) + + for _, node := range nodes { + removeNode(idx, node) + } + + return nil +} + +func removeNode(idx parentIndex, node *yaml.Node) { + parent := idx.getParent(node) + if parent == nil { + return + } + + for i, child := range parent.Content { + if child == node { + switch parent.Kind { + case yaml.MappingNode: + if i%2 == 1 { + // if we select a value, we should delete the key too + parent.Content = append(parent.Content[:i-1], parent.Content[i+1:]...) + } else { + // if we select a key, we should delete the value + parent.Content = append(parent.Content[:i], parent.Content[i+2:]...) + } + return + case yaml.SequenceNode: + parent.Content = append(parent.Content[:i], parent.Content[i+1:]...) + return + } + } + } +} + +// mergeOptions carries version and strictness context through the merge call chain. +type mergeOptions struct { + v110 bool + strict bool +} + +func (o *Overlay) applyUpdateAction(root *yaml.Node, action Action, warnings *[]string, strict bool) error { + if action.Target == "" { + return nil + } + + if action.Update.IsZero() { + return nil + } + + p, err := o.NewPath(action.Target, warnings) + if err != nil { + return err + } + + nodes := p.Query(root) + + opts := mergeOptions{v110: o.IsV110OrLater(), strict: strict} + + // Homogeneity check: 1.1.0 strict mode requires all targets to be the same kind + if strict && opts.v110 && len(nodes) > 1 { + firstKind := nodes[0].Kind + for _, n := range nodes[1:] { + if n.Kind != firstKind { + return fmt.Errorf("target selected mixed node types (%s and %s); all targets must be the same type", + nodeKindName(firstKind), nodeKindName(n.Kind)) + } + } + } + + didMakeChange := false + for _, node := range nodes { + changed, err := applyMerge(node, &action.Update, opts) + if err != nil { + return err + } + didMakeChange = changed || didMakeChange + } + if !didMakeChange { + *warnings = append(*warnings, "does nothing") + } + + return nil +} + +// applyMerge is the top-level merge dispatch for update/copy actions. +// It implements the Action Object rules (spec §4.4.3) which differ from +// the recursive merge rules applied within object properties. +func applyMerge(node *yaml.Node, update *yaml.Node, opts mergeOptions) (bool, error) { + if !opts.v110 { + // 1.0.0: delegate entirely to recursive merge (preserves existing behavior) + return mergeNode(node, update, opts) + } + + // 1.1.0 top-level dispatch per spec §4.4.3: + switch node.Kind { + case yaml.SequenceNode: + // Array target: concatenate if update is array, append if object/primitive + if update.Kind == yaml.SequenceNode { + return mergeSequenceNode(node, update), nil + } + // Append non-array value as single element + node.Content = append(node.Content, clone(update)) + return true, nil + + case yaml.MappingNode: + if update.Kind != yaml.MappingNode { + // Spec: update MUST be an object for object targets + if opts.strict { + return false, fmt.Errorf("target is object but update is %s", nodeKindName(update.Kind)) + } + // Lax: replace gracefully + *node = *clone(update) + return true, nil + } + return mergeNode(node, update, opts) + + default: + // Primitive target: replace + if update.Kind != yaml.ScalarNode && update.Kind != 0 { + if opts.strict { + return false, fmt.Errorf("target is primitive but update is %s", nodeKindName(update.Kind)) + } + // Non-scalar update replaces the whole node + isChanged := node.Value != update.Value || node.Kind != update.Kind + *node = *clone(update) + return isChanged, nil + } + // Scalar-to-scalar: update value only, preserving target's style/tag + isChanged := node.Value != update.Value + node.Value = update.Value + return isChanged, nil + } +} + +// mergeNode is the recursive merge function used within object property merging. +// Type mismatches at this level follow the recursive merge rules, not the +// top-level Action Object rules. +func mergeNode(node *yaml.Node, merge *yaml.Node, opts mergeOptions) (bool, error) { + if node.Kind != merge.Kind { + // 1.1.0 strict: "Other property value combinations are incompatible and result in an error" + if opts.v110 && opts.strict { + return false, fmt.Errorf("type mismatch: target is %s but update is %s", + nodeKindName(node.Kind), nodeKindName(merge.Kind)) + } + // 1.0.0 or 1.1.0 lax: gracefully replace (pre-existing behavior) + *node = *clone(merge) + return true, nil + } + switch node.Kind { + case yaml.MappingNode: + return mergeMappingNode(node, merge, opts) + case yaml.SequenceNode: + return mergeSequenceNode(node, merge), nil + default: + isChanged := node.Value != merge.Value + node.Value = merge.Value + return isChanged, nil + } +} + +// mergeMappingNode will perform a shallow merge of the merge node into the main +// node. +func mergeMappingNode(node *yaml.Node, merge *yaml.Node, opts mergeOptions) (bool, error) { + anyChange := false + + // If the target is an empty flow-style mapping and we're merging content, + // convert to block style for better readability + if len(node.Content) == 0 && node.Style == yaml.FlowStyle && len(merge.Content) > 0 { + node.Style = 0 // Reset to default (block) style + } + +NextKey: + for i := 0; i < len(merge.Content); i += 2 { + mergeKey := merge.Content[i].Value + mergeValue := merge.Content[i+1] + + for j := 0; j < len(node.Content); j += 2 { + nodeKey := node.Content[j].Value + if nodeKey == mergeKey { + changed, err := mergeNode(node.Content[j+1], mergeValue, opts) + if err != nil { + return anyChange, fmt.Errorf("key %q: %w", mergeKey, err) + } + anyChange = changed || anyChange + continue NextKey + } + } + + node.Content = append(node.Content, merge.Content[i], clone(mergeValue)) + anyChange = true + } + return anyChange, nil +} + +// nodeKindName returns a human-readable name for a YAML node kind. +func nodeKindName(kind yaml.Kind) string { + switch kind { + case yaml.MappingNode: + return "object" + case yaml.SequenceNode: + return "array" + case yaml.ScalarNode: + return "scalar" + default: + return "unknown" + } +} + +// mergeSequenceNode will append the merge node's content to the original node. +func mergeSequenceNode(node *yaml.Node, merge *yaml.Node) bool { + node.Content = append(node.Content, clone(merge).Content...) + return true +} + +func clone(node *yaml.Node) *yaml.Node { + newNode := &yaml.Node{ + Kind: node.Kind, + Style: node.Style, + Tag: node.Tag, + Value: node.Value, + Anchor: node.Anchor, + HeadComment: node.HeadComment, + LineComment: node.LineComment, + FootComment: node.FootComment, + } + if node.Alias != nil { + newNode.Alias = clone(node.Alias) + } + if node.Content != nil { + newNode.Content = make([]*yaml.Node, len(node.Content)) + for i, child := range node.Content { + newNode.Content[i] = clone(child) + } + } + return newNode +} + +// applyCopyAction applies a copy action to the document. +func (o *Overlay) applyCopyAction(root *yaml.Node, action Action, warnings *[]string, strict bool) error { + if action.Target == "" { + return nil + } + + if action.Copy == "" { + return nil + } + + // Parse the source path + sourcePath, err := o.NewPath(action.Copy, warnings) + if err != nil { + return fmt.Errorf("invalid copy source path %q: %w", action.Copy, err) + } + + // Query the source nodes + sourceNodes := sourcePath.Query(root) + if len(sourceNodes) == 0 { + // Source not found - in non-strict mode this is silently ignored + // In strict mode, this will be caught by validateSelectorHasAtLeastOneTarget + if warnings != nil { + *warnings = append(*warnings, fmt.Sprintf("copy source %q not found", action.Copy)) + } + return nil + } + + if len(sourceNodes) > 1 { + return fmt.Errorf("copy source path %q matched multiple nodes (%d), expected exactly one", action.Copy, len(sourceNodes)) + } + + sourceNode := sourceNodes[0] + + // Parse the target path + targetPath, err := o.NewPath(action.Target, warnings) + if err != nil { + return fmt.Errorf("invalid target path %q: %w", action.Target, err) + } + + // Query the target nodes + targetNodes := targetPath.Query(root) + + opts := mergeOptions{v110: o.IsV110OrLater(), strict: strict} + + // Homogeneity check: 1.1.0 strict mode requires all targets to be the same kind + if strict && opts.v110 && len(targetNodes) > 1 { + firstKind := targetNodes[0].Kind + for _, n := range targetNodes[1:] { + if n.Kind != firstKind { + return fmt.Errorf("target selected mixed node types (%s and %s); all targets must be the same type", + nodeKindName(firstKind), nodeKindName(n.Kind)) + } + } + } + + // Copy the source node to each target + didMakeChange := false + for _, targetNode := range targetNodes { + // Clone the source node to avoid reference issues + copiedNode := clone(sourceNode) + + // Merge the copied node into the target + changed, err := applyMerge(targetNode, copiedNode, opts) + if err != nil { + return err + } + didMakeChange = changed || didMakeChange + } + + if !didMakeChange && warnings != nil { + *warnings = append(*warnings, "does nothing") + } + + return nil +} diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/compare.go b/vendor/github.com/speakeasy-api/openapi/overlay/compare.go similarity index 97% rename from vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/compare.go rename to vendor/github.com/speakeasy-api/openapi/overlay/compare.go index 33dff6a48..06360a49e 100644 --- a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/compare.go +++ b/vendor/github.com/speakeasy-api/openapi/overlay/compare.go @@ -117,13 +117,13 @@ func walkTreesAndCollectActions(path simplePath, y1 *yaml.Node, y2 yaml.Node) ([ return walkSequenceNode(path, y1, y2) } - if len(y2.Content) == len(y1.Content)+1 && + if len(y2.Content) > len(y1.Content) && yamlEquals(y2.Content[:len(y1.Content)], y1.Content) { return []Action{{ Target: path.ToJSONPath(), Update: yaml.Node{ Kind: y1.Kind, - Content: []*yaml.Node{y2.Content[len(y1.Content)]}, + Content: y2.Content[len(y1.Content):], }, }}, nil } @@ -187,9 +187,13 @@ func walkSequenceNode(path simplePath, y1 *yaml.Node, y2 yaml.Node) ([]Action, e c2 = y2.Content[i] } + var y2Val yaml.Node + if c2 != nil { + y2Val = *c2 + } newActions, err := walkTreesAndCollectActions( path.WithIndex(i), - c1, *c2) + c1, y2Val) if err != nil { return nil, err } diff --git a/vendor/github.com/speakeasy-api/openapi/overlay/jsonpath.go b/vendor/github.com/speakeasy-api/openapi/overlay/jsonpath.go new file mode 100644 index 000000000..2ccc3c736 --- /dev/null +++ b/vendor/github.com/speakeasy-api/openapi/overlay/jsonpath.go @@ -0,0 +1,89 @@ +package overlay + +import ( + "fmt" + + "github.com/speakeasy-api/jsonpath/pkg/jsonpath" + "github.com/speakeasy-api/jsonpath/pkg/jsonpath/config" + "github.com/speakeasy-api/openapi/internal/version" + "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" + "gopkg.in/yaml.v3" +) + +// Queryable is an interface for querying YAML nodes using JSONPath expressions. +type Queryable interface { + Query(root *yaml.Node) []*yaml.Node +} + +type yamlPathQueryable struct { + path *yamlpath.Path +} + +func (y yamlPathQueryable) Query(root *yaml.Node) []*yaml.Node { + if y.path == nil { + return []*yaml.Node{} + } + // errors aren't actually possible from yamlpath. + result, _ := y.path.Find(root) + return result +} + +// NewPath creates a new JSONPath queryable from the given target expression. +// The implementation used depends on the overlay version and JSONPathVersion setting: +// - For version 1.0.0: Legacy yamlpath by default, opt-IN to RFC 9535 via "rfc9535" +// - For version 1.1.0+: RFC 9535 by default, opt-OUT to legacy via "legacy" +func (o *Overlay) NewPath(target string, warnings *[]string) (Queryable, error) { + rfcJSONPath, rfcJSONPathErr := jsonpath.NewPath(target, config.WithPropertyNameExtension()) + if o.UsesRFC9535() { + return rfcJSONPath, rfcJSONPathErr + } + + // For version < 1.1.0 without explicit rfc9535, warn about future incompatibility + if rfcJSONPathErr != nil && warnings != nil { + *warnings = append(*warnings, fmt.Sprintf( + "invalid rfc9535 jsonpath %s: %s\n"+ + "This will be treated as an error in Overlay 1.1.0+. "+ + "Please fix and opt into the new implementation with `\"x-speakeasy-jsonpath\": rfc9535` "+ + "in the root of your overlay, or upgrade to overlay version 1.1.0. "+ + "See overlay.speakeasy.com for an implementation playground.", + target, rfcJSONPathErr.Error())) + } + + path, err := yamlpath.NewPath(target) + return mustExecute(path), err +} + +// UsesRFC9535 determines if the overlay should use RFC 9535 JSONPath implementation. +// +// The behavior depends on the overlay version: +// - For version 1.0.x: RFC 9535 is opt-IN (default is legacy) +// - Set JSONPathVersion to "rfc9535" to enable RFC 9535 +// - For version 1.1.0+: RFC 9535 is the DEFAULT (opt-OUT available) +// - Set JSONPathVersion to "legacy" to use legacy implementation +// +// Explicit settings always take precedence over version-based defaults. +func (o *Overlay) UsesRFC9535() bool { + // Explicit opt-in always works (for both 1.0.0 and 1.1.0) + if o.JSONPathVersion == JSONPathRFC9535 { + return true + } + + // Explicit opt-out always works (for both versions) + if o.JSONPathVersion == JSONPathLegacy { + return false + } + + // No explicit setting - determine based on version + // For version 1.1.0+, RFC 9535 is the default + overlayVersion, err := version.Parse(o.Version) + if err != nil { + return false // Invalid version, use legacy behavior for safety + } + + v110 := version.MustParse("1.1.0") + return !overlayVersion.LessThan(*v110) +} + +func mustExecute(path *yamlpath.Path) yamlPathQueryable { + return yamlPathQueryable{path} +} diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/loader/overlay.go b/vendor/github.com/speakeasy-api/openapi/overlay/loader/overlay.go similarity index 50% rename from vendor/github.com/speakeasy-api/openapi-overlay/pkg/loader/overlay.go rename to vendor/github.com/speakeasy-api/openapi/overlay/loader/overlay.go index 99a8373d5..0a4b19d00 100644 --- a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/loader/overlay.go +++ b/vendor/github.com/speakeasy-api/openapi/overlay/loader/overlay.go @@ -2,7 +2,9 @@ package loader import ( "fmt" - "github.com/speakeasy-api/openapi-overlay/pkg/overlay" + "io" + + "github.com/speakeasy-api/openapi/overlay" ) // LoadOverlay is a tool for loading and parsing an overlay file from the file @@ -15,3 +17,13 @@ func LoadOverlay(path string) (*overlay.Overlay, error) { return o, nil } + +// LoadOverlayFromReader parses an overlay from the given reader. +func LoadOverlayFromReader(r io.Reader) (*overlay.Overlay, error) { + o, err := overlay.ParseReader(r) + if err != nil { + return nil, fmt.Errorf("failed to parse overlay from reader: %w", err) + } + + return o, nil +} diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/loader/spec.go b/vendor/github.com/speakeasy-api/openapi/overlay/loader/spec.go similarity index 57% rename from vendor/github.com/speakeasy-api/openapi-overlay/pkg/loader/spec.go rename to vendor/github.com/speakeasy-api/openapi/overlay/loader/spec.go index 57ec4cb3e..95401ddd1 100644 --- a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/loader/spec.go +++ b/vendor/github.com/speakeasy-api/openapi/overlay/loader/spec.go @@ -1,11 +1,17 @@ package loader import ( + "errors" "fmt" - "github.com/speakeasy-api/openapi-overlay/pkg/overlay" - "gopkg.in/yaml.v3" + "io" "net/url" "os" + "path/filepath" + "runtime" + "strings" + + "github.com/speakeasy-api/openapi/overlay" + "gopkg.in/yaml.v3" ) // GetOverlayExtendsPath returns the path to file if the extends URL is a file @@ -14,7 +20,24 @@ import ( // malformed. func GetOverlayExtendsPath(o *overlay.Overlay) (string, error) { if o.Extends == "" { - return "", fmt.Errorf("overlay does not specify an extends URL") + return "", errors.New("overlay does not specify an extends URL") + } + + // Handle Windows file paths that might be formatted as file URLs + // file:///C:/path or file://C:/path on Windows need special handling + if runtime.GOOS == "windows" && strings.HasPrefix(o.Extends, "file://") { + // Remove the file:// or file:/// prefix + path := strings.TrimPrefix(o.Extends, "file:///") + if path == o.Extends { + path = strings.TrimPrefix(o.Extends, "file://") + } + + // If it looks like a Windows path (e.g., C:/... or C:\...), use it directly + if len(path) >= 2 && path[1] == ':' { + // Convert forward slashes to backslashes for Windows + path = filepath.FromSlash(path) + return path, nil + } } specUrl, err := url.Parse(o.Extends) @@ -26,7 +49,14 @@ func GetOverlayExtendsPath(o *overlay.Overlay) (string, error) { return "", fmt.Errorf("only file:// extends URLs are supported, not %q", o.Extends) } - return specUrl.Path, nil + // On Windows, url.Parse().Path for file:///C:/path returns /C:/path + // We need to strip the leading slash for Windows absolute paths + path := specUrl.Path + if runtime.GOOS == "windows" && len(path) >= 3 && path[0] == '/' && path[2] == ':' { + path = path[1:] // Remove leading slash + } + + return path, nil } // LoadExtendsSpecification will load and parse a YAML or JSON file as specified @@ -41,12 +71,23 @@ func LoadExtendsSpecification(o *overlay.Overlay) (*yaml.Node, error) { return LoadSpecification(path) } +// LoadSpecificationFromReader parses a YAML or JSON specification from the given reader. +func LoadSpecificationFromReader(r io.Reader) (*yaml.Node, error) { + var ys yaml.Node + if err := yaml.NewDecoder(r).Decode(&ys); err != nil { + return nil, fmt.Errorf("failed to parse specification from reader: %w", err) + } + + return &ys, nil +} + // LoadSpecification will load and parse a YAML or JSON file from the given path. func LoadSpecification(path string) (*yaml.Node, error) { - rs, err := os.Open(path) + rs, err := os.Open(path) //nolint:gosec if err != nil { return nil, fmt.Errorf("failed to open schema from path %q: %w", path, err) } + defer rs.Close() var ys yaml.Node err = yaml.NewDecoder(rs).Decode(&ys) diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/parents.go b/vendor/github.com/speakeasy-api/openapi/overlay/parents.go similarity index 100% rename from vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/parents.go rename to vendor/github.com/speakeasy-api/openapi/overlay/parents.go diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/parse.go b/vendor/github.com/speakeasy-api/openapi/overlay/parse.go similarity index 61% rename from vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/parse.go rename to vendor/github.com/speakeasy-api/openapi/overlay/parse.go index 0708846f7..e3fb93c1b 100644 --- a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/parse.go +++ b/vendor/github.com/speakeasy-api/openapi/overlay/parse.go @@ -2,12 +2,28 @@ package overlay import ( "fmt" - "gopkg.in/yaml.v3" "io" "os" "path/filepath" + + "gopkg.in/yaml.v3" ) +// ParseReader parses an overlay from the given reader. +func ParseReader(r io.Reader) (*Overlay, error) { + data, err := io.ReadAll(r) + if err != nil { + return nil, fmt.Errorf("failed to read overlay data: %w", err) + } + + var overlay Overlay + if err := yaml.Unmarshal(data, &overlay); err != nil { + return nil, err + } + + return &overlay, nil +} + // Parse will parse the given reader as an overlay file. func Parse(path string) (*Overlay, error) { filePath, err := filepath.Abs(path) @@ -15,21 +31,18 @@ func Parse(path string) (*Overlay, error) { return nil, fmt.Errorf("failed to get absolute path for %q: %w", path, err) } - ro, err := os.Open(filePath) + data, err := os.ReadFile(filePath) //nolint:gosec if err != nil { - return nil, fmt.Errorf("failed to open overlay file at path %q: %w", path, err) + return nil, fmt.Errorf("failed to read overlay file at path %q: %w", path, err) } - defer ro.Close() var overlay Overlay - dec := yaml.NewDecoder(ro) - - err = dec.Decode(&overlay) + err = yaml.Unmarshal(data, &overlay) if err != nil { return nil, err } - return &overlay, err + return &overlay, nil } // Format will validate reformat the given file @@ -47,7 +60,7 @@ func Format(path string) error { return err } - return os.WriteFile(filePath, []byte(formatted), 0644) + return os.WriteFile(filePath, []byte(formatted), 0600) } // Format writes the file back out as YAML. diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/schema.go b/vendor/github.com/speakeasy-api/openapi/overlay/schema.go similarity index 53% rename from vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/schema.go rename to vendor/github.com/speakeasy-api/openapi/overlay/schema.go index f4408f0fa..1d00e7fbd 100644 --- a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/schema.go +++ b/vendor/github.com/speakeasy-api/openapi/overlay/schema.go @@ -2,9 +2,29 @@ package overlay import ( "bytes" + + "github.com/speakeasy-api/openapi/internal/version" "gopkg.in/yaml.v3" ) +// Version constants for the Overlay specification +const ( + // LatestVersion is the latest supported overlay version + LatestVersion = "1.1.0" + // Version100 is the Overlay 1.0.0 version + Version100 = "1.0.0" + // Version110 is the Overlay 1.1.0 version + Version110 = "1.1.0" +) + +// JSONPath implementation constants +const ( + // JSONPathRFC9535 enables RFC 9535 JSONPath implementation + JSONPathRFC9535 = "rfc9535" + // JSONPathLegacy enables legacy yamlpath implementation (for backward compatibility) + JSONPathLegacy = "legacy" +) + // Extensible provides a place for extensions to be added to components of the // Overlay configuration. These are a map from x-* extension fields to their values. type Extensions map[string]any @@ -13,10 +33,12 @@ type Extensions map[string]any type Overlay struct { Extensions `yaml:"-,inline"` - // Version is the version of the overlay configuration. This is only ever expected to be 1.0.0 + // Version is the version of the overlay configuration (1.0.0 or 1.1.0) Version string `yaml:"overlay"` - // JSONPathVersion should be set to rfc9535, and is used for backwards compatability purposes + // JSONPathVersion controls the JSONPath implementation used. + // For version 1.0.0: default is legacy, use "rfc9535" to opt-in to RFC 9535 + // For version 1.1.0: default is RFC 9535, use "legacy" to opt-out JSONPathVersion string `yaml:"x-speakeasy-jsonpath,omitempty"` // Info describes the metadata for the overlay. @@ -29,6 +51,18 @@ type Overlay struct { Actions []Action `yaml:"actions"` } +// IsV110OrLater returns true if the overlay version is 1.1.0 or later. +// Invalid or missing versions default to false (1.0.0 behavior) for safety. +func (o *Overlay) IsV110OrLater() bool { + overlayVersion, err := version.Parse(o.Version) + if err != nil { + return false + } + + v110 := version.MustParse(Version110) + return !overlayVersion.LessThan(*v110) +} + func (o *Overlay) ToString() (string, error) { buf := bytes.NewBuffer([]byte{}) decoder := yaml.NewEncoder(buf) @@ -46,6 +80,9 @@ type Info struct { // Version is the version of the overlay. Version string `yaml:"version"` + + // Description is an optional description of the overlay (new in Overlay 1.1.0). + Description string `yaml:"description,omitempty"` } type Action struct { @@ -63,4 +100,8 @@ type Action struct { // Remove marks the target node for removal rather than update. Remove bool `yaml:"remove,omitempty"` + + // Copy is a JSONPath to the source node to copy to the target. This is + // mutually exclusive with Update and Remove. + Copy string `yaml:"copy,omitempty"` } diff --git a/vendor/github.com/speakeasy-api/openapi/overlay/upgrade.go b/vendor/github.com/speakeasy-api/openapi/overlay/upgrade.go new file mode 100644 index 000000000..b2cdcdfed --- /dev/null +++ b/vendor/github.com/speakeasy-api/openapi/overlay/upgrade.go @@ -0,0 +1,101 @@ +package overlay + +import ( + "context" + "fmt" + + "github.com/speakeasy-api/openapi/internal/version" +) + +// UpgradeOptions configures the upgrade behavior +type UpgradeOptions struct { + targetVersion string +} + +// Option is a functional option for configuring behavior +type Option[T any] func(*T) + +// WithUpgradeTargetVersion sets the target version for the upgrade. +// If not specified, defaults to the latest supported version (1.1.0). +func WithUpgradeTargetVersion(ver string) Option[UpgradeOptions] { + return func(opts *UpgradeOptions) { + opts.targetVersion = ver + } +} + +// Upgrade upgrades an Overlay document from 1.0.0 to the latest version (1.1.0). +// Returns true if an upgrade was performed, false if no upgrade was needed. +// +// The upgrade process: +// - Updates the overlay version field from 1.0.0 to 1.1.0 +// - Enables RFC 9535 JSONPath as the default implementation +// - Clears redundant x-speakeasy-jsonpath: rfc9535 (now default in 1.1.0) +// - All existing actions remain valid and functional +// +// Note: The upgrade is non-destructive. All 1.0.0 features continue to work in 1.1.0. +func Upgrade(_ context.Context, o *Overlay, opts ...Option[UpgradeOptions]) (bool, error) { + if o == nil { + return false, nil + } + + options := UpgradeOptions{} + for _, opt := range opts { + opt(&options) + } + if options.targetVersion == "" { + options.targetVersion = LatestVersion + } + + currentVersion, err := version.Parse(o.Version) + if err != nil { + return false, fmt.Errorf("invalid current overlay version %q: %w", o.Version, err) + } + + targetVersion, err := version.Parse(options.targetVersion) + if err != nil { + return false, fmt.Errorf("invalid target overlay version %q: %w", options.targetVersion, err) + } + + // Cannot downgrade + if targetVersion.LessThan(*currentVersion) { + return false, fmt.Errorf("cannot downgrade overlay version from %s to %s", + currentVersion, targetVersion) + } + + // Already at target version + if targetVersion.Equal(*currentVersion) { + return false, nil + } + + // Apply upgrade transformations + upgradeFrom100To110(o, currentVersion, targetVersion) + + return true, nil +} + +// upgradeFrom100To110 performs the upgrade from 1.0.0 to 1.1.0 +func upgradeFrom100To110(o *Overlay, currentVersion *version.Version, targetVersion *version.Version) { + v110 := version.MustParse("1.1.0") + + if !currentVersion.LessThan(*v110) { + return // Already at or above 1.1.0 + } + + // Calculate the max version we can upgrade to + maxVersion := v110 + if targetVersion.LessThan(*maxVersion) { + maxVersion = targetVersion + } + + // The upgrade from 1.0.0 to 1.1.0: + // 1. Update the version number + // 2. If x-speakeasy-jsonpath was "rfc9535", it can be cleared (now default) + // 3. No breaking changes - all 1.0.0 overlays remain valid + + // Clear explicit rfc9535 as it's now the default in 1.1.0 + if o.JSONPathVersion == JSONPathRFC9535 { + o.JSONPathVersion = "" + } + + o.Version = maxVersion.String() +} diff --git a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/utils.go b/vendor/github.com/speakeasy-api/openapi/overlay/utils.go similarity index 99% rename from vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/utils.go rename to vendor/github.com/speakeasy-api/openapi/overlay/utils.go index 0ca4054c8..384397066 100644 --- a/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/utils.go +++ b/vendor/github.com/speakeasy-api/openapi/overlay/utils.go @@ -2,6 +2,7 @@ package overlay import ( "fmt" + "gopkg.in/yaml.v3" ) diff --git a/vendor/github.com/speakeasy-api/openapi/overlay/validate.go b/vendor/github.com/speakeasy-api/openapi/overlay/validate.go new file mode 100644 index 000000000..152211121 --- /dev/null +++ b/vendor/github.com/speakeasy-api/openapi/overlay/validate.go @@ -0,0 +1,95 @@ +package overlay + +import ( + "errors" + "fmt" + "net/url" + "strings" + + "github.com/speakeasy-api/openapi/internal/sliceutil" + "github.com/speakeasy-api/openapi/internal/version" +) + +var ( + SupportedVersions = []*version.Version{version.MustParse("1.0.0"), version.MustParse("1.1.0")} +) + +// Errors +var ( + ErrOverlayVersionInvalid = errors.New("overlay version is invalid") + ErrOverlayVersionNotSupported = fmt.Errorf("overlay version must be one of: `%s`", strings.Join(sliceutil.Map(SupportedVersions, func(v *version.Version) string { return v.String() }), ", ")) + ErrOverlayVersionMustBeDefined = errors.New("overlay version must be defined") + ErrOverlayInfoTitleMustBeDefined = errors.New("overlay info title must be defined") + ErrOverlayInfoVersionMustBeDefined = errors.New("overlay info version must be defined") + ErrOverlayExtendsMustBeAValidURL = errors.New("overlay extends must be a valid URL") + ErrOverlayMustDefineAtLeastOneAction = errors.New("overlay must define at least one action") + ErrOverlayActionTargetMustBeDefined = errors.New("overlay action target must be defined") + ErrOverlayActionRemoveAndUpdateCannotBeSet = errors.New("overlay action remove and update cannot be set") +) + +type ValidationErrors []error + +func (v ValidationErrors) Error() string { + msgs := make([]string, len(v)) + for i, err := range v { + msgs[i] = err.Error() + } + return strings.Join(msgs, "\n") +} + +func (v ValidationErrors) Return() error { + if len(v) > 0 { + return v + } + return nil +} + +func (o *Overlay) ValidateVersion() []error { + errs := make(ValidationErrors, 0) + overlayVersion, err := version.Parse(o.Version) + switch { + case err != nil || overlayVersion == nil: + errs = append(errs, ErrOverlayVersionInvalid) + case !overlayVersion.IsOneOf(SupportedVersions): + errs = append(errs, ErrOverlayVersionNotSupported) + } + + return errs +} + +func (o *Overlay) Validate() error { + errs := make(ValidationErrors, 0) + + errs = append(errs, o.ValidateVersion()...) + + if o.Info.Version == "" { + errs = append(errs, errors.New("overlay info version must be defined")) + } + + if o.Info.Title == "" { + errs = append(errs, errors.New("overlay info title must be defined")) + } + + if o.Extends != "" { + _, err := url.Parse(o.Extends) + if err != nil { + errs = append(errs, errors.New("overlay extends must be a valid URL")) + } + } + + if len(o.Actions) == 0 { + errs = append(errs, errors.New("overlay must define at least one action")) + } else { + for i, action := range o.Actions { + if action.Target == "" { + errs = append(errs, fmt.Errorf("overlay action at index %d target must be defined", i)) + } + + if action.Remove && !action.Update.IsZero() { + errs = append(errs, fmt.Errorf("overlay action at index %d should not both set remove and define update", i)) + } + } + } + + return errs.Return() +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 7e19eba09..ffb24e8e3 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -390,7 +390,8 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, failMessage, msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second @@ -403,7 +404,8 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, failMessage, msgAndArgs...) } // Less asserts that the first element is less than the second @@ -415,7 +417,8 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareLess}, failMessage, msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second @@ -428,7 +431,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, failMessage, msgAndArgs...) } // Positive asserts that the specified element is positive @@ -440,7 +444,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, "\"%v\" is not positive", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not positive", e) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, failMessage, msgAndArgs...) } // Negative asserts that the specified element is negative @@ -452,7 +457,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, "\"%v\" is not negative", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not negative", e) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, failMessage, msgAndArgs...) } func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { @@ -468,11 +474,11 @@ func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedCompare compareResult, isComparable := compare(e1, e2, e1Kind) if !isComparable { - return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + return Fail(t, fmt.Sprintf(`Can not compare type "%T"`, e1), msgAndArgs...) } if !containsValue(allowedComparesResults, compareResult) { - return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) + return Fail(t, failMessage, msgAndArgs...) } return true diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 190634165..c592f6ad5 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -50,10 +50,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // assert.Emptyf(t, obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -117,10 +126,8 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// assert.Errorf(t, err, "error message %s", "formatted") func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -438,7 +445,19 @@ func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interf return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) } +// IsNotTypef asserts that the specified objects are not of the same type. +// +// assert.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsNotType(t, theType, object, append([]interface{}{msg}, args...)...) +} + // IsTypef asserts that the specified objects are of the same type. +// +// assert.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted") func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -585,8 +604,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if assert.NotEmptyf(t, obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) @@ -693,12 +711,15 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") // assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// assert.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -782,11 +803,15 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg return Same(t, expected, actual, append([]interface{}{msg}, args...)...) } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") // assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// assert.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 21629087b..58db92845 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -92,10 +92,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st return ElementsMatchf(a.t, listA, listB, msg, args...) } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Empty(obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -103,10 +112,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { return Empty(a.t, object, msgAndArgs...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Emptyf(obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -224,10 +242,8 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// a.Error(err) func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -297,10 +313,8 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// a.Errorf(err, "error message %s", "formatted") func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -868,7 +882,29 @@ func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...in return IsNonIncreasingf(a.t, object, msg, args...) } +// IsNotType asserts that the specified objects are not of the same type. +// +// a.IsNotType(&NotMyStruct{}, &MyStruct{}) +func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNotType(a.t, theType, object, msgAndArgs...) +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNotTypef(a.t, theType, object, msg, args...) +} + // IsType asserts that the specified objects are of the same type. +// +// a.IsType(&MyStruct{}, &MyStruct{}) func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -877,6 +913,8 @@ func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAnd } // IsTypef asserts that the specified objects are of the same type. +// +// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted") func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1162,8 +1200,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg return NotElementsMatchf(a.t, listA, listB, msg, args...) } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) @@ -1175,8 +1212,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo return NotEmpty(a.t, object, msgAndArgs...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) @@ -1378,12 +1414,15 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri return NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubset([1, 3, 4], [1, 2]) // a.NotSubset({"x": 1, "y": 2}, {"z": 3}) +// a.NotSubset([1, 3, 4], {1: "one", 2: "two"}) +// a.NotSubset({"x": 1, "y": 2}, ["z"]) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1391,12 +1430,15 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs return NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") // a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1556,11 +1598,15 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, return Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subset([1, 2, 3], [1, 2]) // a.Subset({"x": 1, "y": 2}, {"x": 1}) +// a.Subset([1, 2, 3], {1: "one", 2: "two"}) +// a.Subset({"x": 1, "y": 2}, ["x"]) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1568,11 +1614,15 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... return Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") // a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 1d2f71824..2fdf80fdd 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -33,7 +33,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareR compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) if !isComparable { - return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) + return Fail(t, fmt.Sprintf(`Can not compare type "%T" and "%T"`, value, prevValue), msgAndArgs...) } if !containsValue(allowedComparesResults, compareResult) { diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 4e91332bb..de8de0cb6 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -210,59 +210,77 @@ the problem actually occurred in calling code.*/ // of each stack frame leading from the current test to the assert call that // failed. func CallerInfo() []string { - var pc uintptr - var ok bool var file string var line int var name string + const stackFrameBufferSize = 10 + pcs := make([]uintptr, stackFrameBufferSize) + callers := []string{} - for i := 0; ; i++ { - pc, file, line, ok = runtime.Caller(i) - if !ok { - // The breaks below failed to terminate the loop, and we ran off the - // end of the call stack. - break - } + offset := 1 - // This is a huge edge case, but it will panic if this is the case, see #180 - if file == "" { - break - } + for { + n := runtime.Callers(offset, pcs) - f := runtime.FuncForPC(pc) - if f == nil { - break - } - name = f.Name() - - // testing.tRunner is the standard library function that calls - // tests. Subtests are called directly by tRunner, without going through - // the Test/Benchmark/Example function that contains the t.Run calls, so - // with subtests we should break when we hit tRunner, without adding it - // to the list of callers. - if name == "testing.tRunner" { + if n == 0 { break } - parts := strings.Split(file, "/") - if len(parts) > 1 { - filename := parts[len(parts)-1] - dir := parts[len(parts)-2] - if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { - callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + frames := runtime.CallersFrames(pcs[:n]) + + for { + frame, more := frames.Next() + pc = frame.PC + file = frame.File + line = frame.Line + + // This is a huge edge case, but it will panic if this is the case, see #180 + if file == "" { + break } - } - // Drop the package - segments := strings.Split(name, ".") - name = segments[len(segments)-1] - if isTest(name, "Test") || - isTest(name, "Benchmark") || - isTest(name, "Example") { - break + f := runtime.FuncForPC(pc) + if f == nil { + break + } + name = f.Name() + + // testing.tRunner is the standard library function that calls + // tests. Subtests are called directly by tRunner, without going through + // the Test/Benchmark/Example function that contains the t.Run calls, so + // with subtests we should break when we hit tRunner, without adding it + // to the list of callers. + if name == "testing.tRunner" { + break + } + + parts := strings.Split(file, "/") + if len(parts) > 1 { + filename := parts[len(parts)-1] + dir := parts[len(parts)-2] + if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { + callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + } + } + + // Drop the package + dotPos := strings.LastIndexByte(name, '.') + name = name[dotPos+1:] + if isTest(name, "Test") || + isTest(name, "Benchmark") || + isTest(name, "Example") { + break + } + + if !more { + break + } } + + // Next batch + offset += cap(pcs) } return callers @@ -437,17 +455,34 @@ func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, return true } +func isType(expectedType, object interface{}) bool { + return ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) +} + // IsType asserts that the specified objects are of the same type. -func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { +// +// assert.IsType(t, &MyStruct{}, &MyStruct{}) +func IsType(t TestingT, expectedType, object interface{}, msgAndArgs ...interface{}) bool { + if isType(expectedType, object) { + return true + } if h, ok := t.(tHelper); ok { h.Helper() } + return Fail(t, fmt.Sprintf("Object expected to be of type %T, but was %T", expectedType, object), msgAndArgs...) +} - if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { - return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) +// IsNotType asserts that the specified objects are not of the same type. +// +// assert.IsNotType(t, &NotMyStruct{}, &MyStruct{}) +func IsNotType(t TestingT, theType, object interface{}, msgAndArgs ...interface{}) bool { + if !isType(theType, object) { + return true } - - return true + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, fmt.Sprintf("Object type expected to be different than %T", theType), msgAndArgs...) } // Equal asserts that two objects are equal. @@ -475,7 +510,6 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) } return true - } // validateEqualArgs checks whether provided arguments can be safely used in the @@ -510,8 +544,9 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b if !same { // both are pointers but not the same type & pointing to the same address return Fail(t, fmt.Sprintf("Not same: \n"+ - "expected: %p %#v\n"+ - "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) + "expected: %p %#[1]v\n"+ + "actual : %p %#[2]v", + expected, actual), msgAndArgs...) } return true @@ -530,14 +565,14 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} same, ok := samePointers(expected, actual) if !ok { - //fails when the arguments are not pointers + // fails when the arguments are not pointers return !(Fail(t, "Both arguments must be pointers", msgAndArgs...)) } if same { return Fail(t, fmt.Sprintf( - "Expected and actual point to the same object: %p %#v", - expected, expected), msgAndArgs...) + "Expected and actual point to the same object: %p %#[1]v", + expected), msgAndArgs...) } return true } @@ -549,7 +584,7 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} func samePointers(first, second interface{}) (same bool, ok bool) { firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { - return false, false //not both are pointers + return false, false // not both are pointers } firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) @@ -610,7 +645,6 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa } return true - } // EqualExportedValues asserts that the types of two objects are equal and their public @@ -665,7 +699,6 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} } return Equal(t, expected, actual, msgAndArgs...) - } // NotNil asserts that the specified object is not nil. @@ -715,37 +748,45 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { // isEmpty gets whether the specified object is considered empty or not. func isEmpty(object interface{}) bool { - // get nil case out of the way if object == nil { return true } - objValue := reflect.ValueOf(object) + return isEmptyValue(reflect.ValueOf(object)) +} +// isEmptyValue gets whether the specified reflect.Value is considered empty or not. +func isEmptyValue(objValue reflect.Value) bool { + if objValue.IsZero() { + return true + } + // Special cases of non-zero values that we consider empty switch objValue.Kind() { // collection types are empty when they have no element + // Note: array types are empty when they match their zero-initialized state. case reflect.Chan, reflect.Map, reflect.Slice: return objValue.Len() == 0 - // pointers are empty if nil or if the value they point to is empty + // non-nil pointers are empty if the value they point to is empty case reflect.Ptr: - if objValue.IsNil() { - return true - } - deref := objValue.Elem().Interface() - return isEmpty(deref) - // for all other types, compare against the zero value - // array types are empty when they match their zero-initialized state - default: - zero := reflect.Zero(objValue.Type()) - return reflect.DeepEqual(object, zero.Interface()) + return isEmptyValue(objValue.Elem()) } + return false } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // assert.Empty(t, obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { @@ -756,11 +797,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { } return pass - } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if assert.NotEmpty(t, obj) { // assert.Equal(t, "two", obj[1]) @@ -775,7 +814,6 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { } return pass - } // getLen tries to get the length of an object. @@ -819,7 +857,6 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { } return true - } // False asserts that the specified value is false. @@ -834,7 +871,6 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { } return true - } // NotEqual asserts that the specified values are NOT equal. @@ -857,7 +893,6 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ } return true - } // NotEqualValues asserts that two objects are not equal even when converted to the same type @@ -880,7 +915,6 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte // return (true, false) if element was not found. // return (true, true) if element was found. func containsElement(list interface{}, element interface{}) (ok, found bool) { - listValue := reflect.ValueOf(list) listType := reflect.TypeOf(list) if listType == nil { @@ -915,7 +949,6 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) { } } return true, false - } // Contains asserts that the specified string, list(array, slice...) or map contains the @@ -938,7 +971,6 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo } return true - } // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the @@ -961,14 +993,17 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) } return true - } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.Subset(t, [1, 2, 3], [1, 2]) // assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) +// assert.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) +// assert.Subset(t, {"x": 1, "y": 2}, ["x"]) func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -983,7 +1018,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok } subsetKind := reflect.TypeOf(subset).Kind() - if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } @@ -1007,6 +1042,13 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok } subsetList := reflect.ValueOf(subset) + if subsetKind == reflect.Map { + keys := make([]interface{}, subsetList.Len()) + for idx, key := range subsetList.MapKeys() { + keys[idx] = key.Interface() + } + subsetList = reflect.ValueOf(keys) + } for i := 0; i < subsetList.Len(); i++ { element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) @@ -1021,12 +1063,15 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.NotSubset(t, [1, 3, 4], [1, 2]) // assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) +// assert.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) +// assert.NotSubset(t, {"x": 1, "y": 2}, ["z"]) func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1041,7 +1086,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) } subsetKind := reflect.TypeOf(subset).Kind() - if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } @@ -1065,11 +1110,18 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) } subsetList := reflect.ValueOf(subset) + if subsetKind == reflect.Map { + keys := make([]interface{}, subsetList.Len()) + for idx, key := range subsetList.MapKeys() { + keys[idx] = key.Interface() + } + subsetList = reflect.ValueOf(keys) + } for i := 0; i < subsetList.Len(); i++ { element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", list), msgAndArgs...) } if !found { return true @@ -1591,10 +1643,8 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// assert.Error(t, err) func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { if err == nil { if h, ok := t.(tHelper); ok { @@ -1667,7 +1717,6 @@ func matchRegexp(rx interface{}, str interface{}) bool { default: return r.MatchString(fmt.Sprint(v)) } - } // Regexp asserts that a specified regexp matches a string. @@ -1703,7 +1752,6 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf } return !match - } // Zero asserts that i is the zero value for its type. @@ -1814,6 +1862,11 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) } + // Shortcut if same bytes + if actual == expected { + return true + } + if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) } @@ -1832,6 +1885,11 @@ func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) } + // Shortcut if same bytes + if actual == expected { + return true + } + if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) } @@ -1933,6 +1991,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t } ch := make(chan bool, 1) + checkCond := func() { ch <- condition() } timer := time.NewTimer(waitFor) defer timer.Stop() @@ -1940,18 +1999,23 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t ticker := time.NewTicker(tick) defer ticker.Stop() - for tick := ticker.C; ; { + var tickC <-chan time.Time + + // Check the condition once first on the initial call. + go checkCond() + + for { select { case <-timer.C: return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tick: - tick = nil - go func() { ch <- condition() }() + case <-tickC: + tickC = nil + go checkCond() case v := <-ch: if v { return true } - tick = ticker.C + tickC = ticker.C } } } @@ -1964,6 +2028,9 @@ type CollectT struct { errors []error } +// Helper is like [testing.T.Helper] but does nothing. +func (CollectT) Helper() {} + // Errorf collects the error. func (c *CollectT) Errorf(format string, args ...interface{}) { c.errors = append(c.errors, fmt.Errorf(format, args...)) @@ -2021,35 +2088,42 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time var lastFinishedTickErrs []error ch := make(chan *CollectT, 1) + checkCond := func() { + collect := new(CollectT) + defer func() { + ch <- collect + }() + condition(collect) + } + timer := time.NewTimer(waitFor) defer timer.Stop() ticker := time.NewTicker(tick) defer ticker.Stop() - for tick := ticker.C; ; { + var tickC <-chan time.Time + + // Check the condition once first on the initial call. + go checkCond() + + for { select { case <-timer.C: for _, err := range lastFinishedTickErrs { t.Errorf("%v", err) } return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tick: - tick = nil - go func() { - collect := new(CollectT) - defer func() { - ch <- collect - }() - condition(collect) - }() + case <-tickC: + tickC = nil + go checkCond() case collect := <-ch: if !collect.failed() { return true } // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. lastFinishedTickErrs = collect.errors - tick = ticker.C + tickC = ticker.C } } } @@ -2064,6 +2138,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D } ch := make(chan bool, 1) + checkCond := func() { ch <- condition() } timer := time.NewTimer(waitFor) defer timer.Stop() @@ -2071,18 +2146,23 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D ticker := time.NewTicker(tick) defer ticker.Stop() - for tick := ticker.C; ; { + var tickC <-chan time.Time + + // Check the condition once first on the initial call. + go checkCond() + + for { select { case <-timer.C: return true - case <-tick: - tick = nil - go func() { ch <- condition() }() + case <-tickC: + tickC = nil + go checkCond() case v := <-ch: if v { return Fail(t, "Condition satisfied", msgAndArgs...) } - tick = ticker.C + tickC = ticker.C } } } @@ -2100,9 +2180,12 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { var expectedText string if target != nil { expectedText = target.Error() + if err == nil { + return Fail(t, fmt.Sprintf("Expected error with %q in chain but got nil.", expectedText), msgAndArgs...) + } } - chain := buildErrorChainString(err) + chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ "expected: %q\n"+ @@ -2125,7 +2208,7 @@ func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { expectedText = target.Error() } - chain := buildErrorChainString(err) + chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ "found: %q\n"+ @@ -2143,11 +2226,17 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{ return true } - chain := buildErrorChainString(err) + expectedType := reflect.TypeOf(target).Elem().String() + if err == nil { + return Fail(t, fmt.Sprintf("An error is expected but got nil.\n"+ + "expected: %s", expectedType), msgAndArgs...) + } + + chain := buildErrorChainString(err, true) return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ - "expected: %q\n"+ - "in chain: %s", target, chain, + "expected: %s\n"+ + "in chain: %s", expectedType, chain, ), msgAndArgs...) } @@ -2161,24 +2250,46 @@ func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interfa return true } - chain := buildErrorChainString(err) + chain := buildErrorChainString(err, true) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ - "found: %q\n"+ - "in chain: %s", target, chain, + "found: %s\n"+ + "in chain: %s", reflect.TypeOf(target).Elem().String(), chain, ), msgAndArgs...) } -func buildErrorChainString(err error) string { +func unwrapAll(err error) (errs []error) { + errs = append(errs, err) + switch x := err.(type) { + case interface{ Unwrap() error }: + err = x.Unwrap() + if err == nil { + return + } + errs = append(errs, unwrapAll(err)...) + case interface{ Unwrap() []error }: + for _, err := range x.Unwrap() { + errs = append(errs, unwrapAll(err)...) + } + } + return +} + +func buildErrorChainString(err error, withType bool) string { if err == nil { return "" } - e := errors.Unwrap(err) - chain := fmt.Sprintf("%q", err.Error()) - for e != nil { - chain += fmt.Sprintf("\n\t%q", e.Error()) - e = errors.Unwrap(e) + var chain string + errs := unwrapAll(err) + for i := range errs { + if i != 0 { + chain += "\n\t" + } + chain += fmt.Sprintf("%q", errs[i].Error()) + if withType { + chain += fmt.Sprintf(" (%T)", errs[i]) + } } return chain } diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go index 4953981d3..a0b953aa5 100644 --- a/vendor/github.com/stretchr/testify/assert/doc.go +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -1,5 +1,9 @@ // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. // +// # Note +// +// All functions in this package return a bool value indicating whether the assertion has passed. +// // # Example Usage // // The following is a complete example using assert in a standard test function: diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index 861ed4b7c..5a6bb75f2 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -138,7 +138,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, contains := strings.Contains(body, fmt.Sprint(str)) if !contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) + Fail(t, fmt.Sprintf("Expected response body for %q to contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) } return contains @@ -158,7 +158,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin contains := strings.Contains(body, fmt.Sprint(str)) if contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) + Fail(t, fmt.Sprintf("Expected response body for %q to NOT contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) } return !contains diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go index baa0cc7d7..5a74c4f4d 100644 --- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go @@ -1,5 +1,4 @@ //go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default -// +build testify_yaml_custom,!testify_yaml_fail,!testify_yaml_default // Package yaml is an implementation of YAML functions that calls a pluggable implementation. // diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go index b83c6cf64..0bae80e34 100644 --- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go @@ -1,5 +1,4 @@ //go:build !testify_yaml_fail && !testify_yaml_custom -// +build !testify_yaml_fail,!testify_yaml_custom // Package yaml is just an indirection to handle YAML deserialization. // diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go index e78f7dfe6..8041803fd 100644 --- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go @@ -1,5 +1,4 @@ //go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default -// +build testify_yaml_fail,!testify_yaml_custom,!testify_yaml_default // Package yaml is an implementation of YAML functions that always fail. // diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go index 968434724..c8e3f94a8 100644 --- a/vendor/github.com/stretchr/testify/require/doc.go +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -23,6 +23,8 @@ // // The `require` package have same global functions as in the `assert` package, // but instead of returning a boolean result they call `t.FailNow()`. +// A consequence of this is that it must be called from the goroutine running +// the test function, not from other goroutines created during the test. // // Every assertion function also takes an optional string message as the final argument, // allowing custom error messages to be appended to the message the assertion method outputs. diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index d8921950d..2d02f9bce 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -117,10 +117,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string t.FailNow() } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // require.Empty(t, obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -131,10 +140,19 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { t.FailNow() } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // require.Emptyf(t, obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -279,10 +297,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if require.Error(t, err) { -// require.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// require.Error(t, err) func Error(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -373,10 +389,8 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if require.Errorf(t, err, "error message %s", "formatted") { -// require.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// require.Errorf(t, err, "error message %s", "formatted") func Errorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1097,7 +1111,35 @@ func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interf t.FailNow() } +// IsNotType asserts that the specified objects are not of the same type. +// +// require.IsNotType(t, &NotMyStruct{}, &MyStruct{}) +func IsNotType(t TestingT, theType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNotType(t, theType, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// require.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNotTypef(t, theType, object, msg, args...) { + return + } + t.FailNow() +} + // IsType asserts that the specified objects are of the same type. +// +// require.IsType(t, &MyStruct{}, &MyStruct{}) func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1109,6 +1151,8 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs } // IsTypef asserts that the specified objects are of the same type. +// +// require.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted") func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1469,8 +1513,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str t.FailNow() } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if require.NotEmpty(t, obj) { // require.Equal(t, "two", obj[1]) @@ -1485,8 +1528,7 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { t.FailNow() } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if require.NotEmptyf(t, obj, "error message %s", "formatted") { // require.Equal(t, "two", obj[1]) @@ -1745,12 +1787,15 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // require.NotSubset(t, [1, 3, 4], [1, 2]) // require.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) +// require.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) +// require.NotSubset(t, {"x": 1, "y": 2}, ["z"]) func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1761,12 +1806,15 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i t.FailNow() } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // require.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") // require.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// require.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// require.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1971,11 +2019,15 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg t.FailNow() } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // require.Subset(t, [1, 2, 3], [1, 2]) // require.Subset(t, {"x": 1, "y": 2}, {"x": 1}) +// require.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) +// require.Subset(t, {"x": 1, "y": 2}, ["x"]) func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1986,11 +2038,15 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte t.FailNow() } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // require.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") // require.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// require.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// require.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 1bd87304f..e6f7e9446 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -93,10 +93,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st ElementsMatchf(a.t, listA, listB, msg, args...) } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Empty(obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -104,10 +113,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { Empty(a.t, object, msgAndArgs...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Emptyf(obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -225,10 +243,8 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// a.Error(err) func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -298,10 +314,8 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// a.Errorf(err, "error message %s", "formatted") func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -869,7 +883,29 @@ func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...in IsNonIncreasingf(a.t, object, msg, args...) } +// IsNotType asserts that the specified objects are not of the same type. +// +// a.IsNotType(&NotMyStruct{}, &MyStruct{}) +func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNotType(a.t, theType, object, msgAndArgs...) +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNotTypef(a.t, theType, object, msg, args...) +} + // IsType asserts that the specified objects are of the same type. +// +// a.IsType(&MyStruct{}, &MyStruct{}) func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -878,6 +914,8 @@ func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAnd } // IsTypef asserts that the specified objects are of the same type. +// +// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted") func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1163,8 +1201,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg NotElementsMatchf(a.t, listA, listB, msg, args...) } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) @@ -1176,8 +1213,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { NotEmpty(a.t, object, msgAndArgs...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) @@ -1379,12 +1415,15 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubset([1, 3, 4], [1, 2]) // a.NotSubset({"x": 1, "y": 2}, {"z": 3}) +// a.NotSubset([1, 3, 4], {1: "one", 2: "two"}) +// a.NotSubset({"x": 1, "y": 2}, ["z"]) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1392,12 +1431,15 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") // a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1557,11 +1599,15 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subset([1, 2, 3], [1, 2]) // a.Subset({"x": 1, "y": 2}, {"x": 1}) +// a.Subset([1, 2, 3], {1: "one", 2: "two"}) +// a.Subset({"x": 1, "y": 2}, ["x"]) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1569,11 +1615,15 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") // a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/tidepool-org/clinic/client/types.go b/vendor/github.com/tidepool-org/clinic/client/types.go index f3cbf2283..7c114f770 100644 --- a/vendor/github.com/tidepool-org/clinic/client/types.go +++ b/vendor/github.com/tidepool-org/clinic/client/types.go @@ -1418,6 +1418,12 @@ type EhrMatchResponseV1 struct { Settings EhrSettingsV1 `json:"settings"` } +// EhrNoteSettingsV1 defines model for ehrNoteSettings.v1. +type EhrNoteSettingsV1 struct { + // IncludeGMI If true, include GMI in the notes. + IncludeGMI bool `json:"includeGMI,omitzero"` +} + // EhrProceduresV1 defines model for ehrProcedures.v1. type EhrProceduresV1 struct { CreateAccount *string `json:"createAccount,omitempty"` @@ -1436,6 +1442,7 @@ type EhrSettingsV1 struct { Enabled bool `json:"enabled"` Flowsheets EhrFlowsheetSettingsV1 `json:"flowsheets"` MrnIdType string `json:"mrnIdType"` + Notes EhrNoteSettingsV1 `json:"notes,omitzero"` ProcedureCodes EhrProceduresV1 `json:"procedureCodes"` Provider EhrSettingsV1Provider `json:"provider"` diff --git a/vendor/gopkg.in/yaml.v2/LICENSE.libyaml b/vendor/go.yaml.in/yaml/v3/LICENSE similarity index 51% rename from vendor/gopkg.in/yaml.v2/LICENSE.libyaml rename to vendor/go.yaml.in/yaml/v3/LICENSE index 8da58fbf6..2683e4bb1 100644 --- a/vendor/gopkg.in/yaml.v2/LICENSE.libyaml +++ b/vendor/go.yaml.in/yaml/v3/LICENSE @@ -1,16 +1,17 @@ + +This project is covered by two different licenses: MIT and Apache. + +#### MIT License #### + The following files were ported to Go from C files of libyaml, and thus -are still covered by their original copyright and license: +are still covered by their original MIT license, with the additional +copyright staring in 2011 when the project was ported over: - apic.go - emitterc.go - parserc.go - readerc.go - scannerc.go - writerc.go - yamlh.go - yamlprivateh.go + apic.go emitterc.go parserc.go readerc.go scannerc.go + writerc.go yamlh.go yamlprivateh.go -Copyright (c) 2006 Kirill Simonov +Copyright (c) 2006-2010 Kirill Simonov +Copyright (c) 2006-2011 Kirill Simonov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -29,3 +30,21 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +### Apache License ### + +All the remaining project files are covered by the Apache license: + +Copyright (c) 2011-2019 Canonical Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/gopkg.in/yaml.v2/NOTICE b/vendor/go.yaml.in/yaml/v3/NOTICE similarity index 100% rename from vendor/gopkg.in/yaml.v2/NOTICE rename to vendor/go.yaml.in/yaml/v3/NOTICE diff --git a/vendor/go.yaml.in/yaml/v3/README.md b/vendor/go.yaml.in/yaml/v3/README.md new file mode 100644 index 000000000..15a85a635 --- /dev/null +++ b/vendor/go.yaml.in/yaml/v3/README.md @@ -0,0 +1,171 @@ +go.yaml.in/yaml +=============== + +YAML Support for the Go Language + + +## Introduction + +The `yaml` package enables [Go](https://go.dev/) programs to comfortably encode +and decode [YAML](https://yaml.org/) values. + +It was originally developed within [Canonical](https://www.canonical.com) as +part of the [juju](https://juju.ubuntu.com) project, and is based on a pure Go +port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) C library to +parse and generate YAML data quickly and reliably. + + +## Project Status + +This project started as a fork of the extremely popular [go-yaml]( +https://github.com/go-yaml/yaml/) +project, and is being maintained by the official [YAML organization]( +https://github.com/yaml/). + +The YAML team took over ongoing maintenance and development of the project after +discussion with go-yaml's author, @niemeyer, following his decision to +[label the project repository as "unmaintained"]( +https://github.com/go-yaml/yaml/blob/944c86a7d2/README.md) in April 2025. + +We have put together a team of dedicated maintainers including representatives +of go-yaml's most important downstream projects. + +We will strive to earn the trust of the various go-yaml forks to switch back to +this repository as their upstream. + +Please [contact us](https://cloud-native.slack.com/archives/C08PPAT8PS7) if you +would like to contribute or be involved. + + +## Compatibility + +The `yaml` package supports most of YAML 1.2, but preserves some behavior from +1.1 for backwards compatibility. + +Specifically, v3 of the `yaml` package: + +* Supports YAML 1.1 bools (`yes`/`no`, `on`/`off`) as long as they are being + decoded into a typed bool value. + Otherwise they behave as a string. + Booleans in YAML 1.2 are `true`/`false` only. +* Supports octals encoded and decoded as `0777` per YAML 1.1, rather than + `0o777` as specified in YAML 1.2, because most parsers still use the old + format. + Octals in the `0o777` format are supported though, so new files work. +* Does not support base-60 floats. + These are gone from YAML 1.2, and were actually never supported by this + package as it's clearly a poor choice. + + +## Installation and Usage + +The import path for the package is *go.yaml.in/yaml/v3*. + +To install it, run: + +```bash +go get go.yaml.in/yaml/v3 +``` + + +## API Documentation + +See: + + +## API Stability + +The package API for yaml v3 will remain stable as described in [gopkg.in]( +https://gopkg.in). + + +## Example + +```go +package main + +import ( + "fmt" + "log" + + "go.yaml.in/yaml/v3" +) + +var data = ` +a: Easy! +b: + c: 2 + d: [3, 4] +` + +// Note: struct fields must be public in order for unmarshal to +// correctly populate the data. +type T struct { + A string + B struct { + RenamedC int `yaml:"c"` + D []int `yaml:",flow"` + } +} + +func main() { + t := T{} + + err := yaml.Unmarshal([]byte(data), &t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t:\n%v\n\n", t) + + d, err := yaml.Marshal(&t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t dump:\n%s\n\n", string(d)) + + m := make(map[interface{}]interface{}) + + err = yaml.Unmarshal([]byte(data), &m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m:\n%v\n\n", m) + + d, err = yaml.Marshal(&m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m dump:\n%s\n\n", string(d)) +} +``` + +This example will generate the following output: + +``` +--- t: +{Easy! {2 [3 4]}} + +--- t dump: +a: Easy! +b: + c: 2 + d: [3, 4] + + +--- m: +map[a:Easy! b:map[c:2 d:[3 4]]] + +--- m dump: +a: Easy! +b: + c: 2 + d: + - 3 + - 4 +``` + + +## License + +The yaml package is licensed under the MIT and Apache License 2.0 licenses. +Please see the LICENSE file for details. diff --git a/vendor/gopkg.in/yaml.v2/apic.go b/vendor/go.yaml.in/yaml/v3/apic.go similarity index 93% rename from vendor/gopkg.in/yaml.v2/apic.go rename to vendor/go.yaml.in/yaml/v3/apic.go index acf71402c..05fd305da 100644 --- a/vendor/gopkg.in/yaml.v2/apic.go +++ b/vendor/go.yaml.in/yaml/v3/apic.go @@ -1,3 +1,25 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + package yaml import ( @@ -79,8 +101,6 @@ func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { parser.encoding = encoding } -var disableLineWrapping = false - // Create a new emitter object. func yaml_emitter_initialize(emitter *yaml_emitter_t) { *emitter = yaml_emitter_t{ @@ -88,9 +108,7 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) { raw_buffer: make([]byte, 0, output_raw_buffer_size), states: make([]yaml_emitter_state_t, 0, initial_stack_size), events: make([]yaml_event_t, 0, initial_queue_size), - } - if disableLineWrapping { - emitter.best_width = -1 + best_width: -1, } } @@ -143,7 +161,7 @@ func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { emitter.canonical = canonical } -//// Set the indentation increment. +// Set the indentation increment. func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { if indent < 2 || indent > 9 { indent = 2 @@ -293,29 +311,14 @@ func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { } } -///* -// * Create ALIAS. -// */ -// -//YAML_DECLARE(int) -//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) -//{ -// mark yaml_mark_t = { 0, 0, 0 } -// anchor_copy *yaml_char_t = NULL -// -// assert(event) // Non-NULL event object is expected. -// assert(anchor) // Non-NULL anchor is expected. -// -// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 -// -// anchor_copy = yaml_strdup(anchor) -// if (!anchor_copy) -// return 0 -// -// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) -// -// return 1 -//} +// Create ALIAS. +func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { + *event = yaml_event_t{ + typ: yaml_ALIAS_EVENT, + anchor: anchor, + } + return true +} // Create SCALAR. func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/go.yaml.in/yaml/v3/decode.go similarity index 50% rename from vendor/gopkg.in/yaml.v2/decode.go rename to vendor/go.yaml.in/yaml/v3/decode.go index 129bc2a97..02e2b17bf 100644 --- a/vendor/gopkg.in/yaml.v2/decode.go +++ b/vendor/go.yaml.in/yaml/v3/decode.go @@ -1,3 +1,18 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package yaml import ( @@ -11,34 +26,16 @@ import ( "time" ) -const ( - documentNode = 1 << iota - mappingNode - sequenceNode - scalarNode - aliasNode -) - -type node struct { - kind int - line, column int - tag string - // For an alias node, alias holds the resolved alias. - alias *node - value string - implicit bool - children []*node - anchors map[string]*node -} - // ---------------------------------------------------------------------------- // Parser, produces a node tree out of a libyaml event stream. type parser struct { parser yaml_parser_t event yaml_event_t - doc *node + doc *Node + anchors map[string]*Node doneInit bool + textless bool } func newParser(b []byte) *parser { @@ -66,6 +63,7 @@ func (p *parser) init() { if p.doneInit { return } + p.anchors = make(map[string]*Node) p.expect(yaml_STREAM_START_EVENT) p.doneInit = true } @@ -102,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t { if p.event.typ != yaml_NO_EVENT { return p.event.typ } - if !yaml_parser_parse(&p.parser, &p.event) { + // It's curious choice from the underlying API to generally return a + // positive result on success, but on this case return true in an error + // scenario. This was the source of bugs in the past (issue #666). + if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { p.fail() } return p.event.typ @@ -111,14 +112,18 @@ func (p *parser) peek() yaml_event_type_t { func (p *parser) fail() { var where string var line int - if p.parser.problem_mark.line != 0 { + if p.parser.context_mark.line != 0 { + line = p.parser.context_mark.line + // Scanner errors don't iterate line before returning error + if p.parser.error == yaml_SCANNER_ERROR { + line++ + } + } else if p.parser.problem_mark.line != 0 { line = p.parser.problem_mark.line // Scanner errors don't iterate line before returning error if p.parser.error == yaml_SCANNER_ERROR { line++ } - } else if p.parser.context_mark.line != 0 { - line = p.parser.context_mark.line } if line != 0 { where = "line " + strconv.Itoa(line) + ": " @@ -132,13 +137,14 @@ func (p *parser) fail() { failf("%s%s", where, msg) } -func (p *parser) anchor(n *node, anchor []byte) { +func (p *parser) anchor(n *Node, anchor []byte) { if anchor != nil { - p.doc.anchors[string(anchor)] = n + n.Anchor = string(anchor) + p.anchors[n.Anchor] = n } } -func (p *parser) parse() *node { +func (p *parser) parse() *Node { p.init() switch p.peek() { case yaml_SCALAR_EVENT: @@ -154,67 +160,148 @@ func (p *parser) parse() *node { case yaml_STREAM_END_EVENT: // Happens when attempting to decode an empty buffer. return nil + case yaml_TAIL_COMMENT_EVENT: + panic("internal error: unexpected tail comment event (please report)") default: - panic("attempted to parse unknown event: " + p.event.typ.String()) + panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) } } -func (p *parser) node(kind int) *node { - return &node{ - kind: kind, - line: p.event.start_mark.line, - column: p.event.start_mark.column, +func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { + var style Style + if tag != "" && tag != "!" { + tag = shortTag(tag) + style = TaggedStyle + } else if defaultTag != "" { + tag = defaultTag + } else if kind == ScalarNode { + tag, _ = resolve("", value) } + n := &Node{ + Kind: kind, + Tag: tag, + Value: value, + Style: style, + } + if !p.textless { + n.Line = p.event.start_mark.line + 1 + n.Column = p.event.start_mark.column + 1 + n.HeadComment = string(p.event.head_comment) + n.LineComment = string(p.event.line_comment) + n.FootComment = string(p.event.foot_comment) + } + return n +} + +func (p *parser) parseChild(parent *Node) *Node { + child := p.parse() + parent.Content = append(parent.Content, child) + return child } -func (p *parser) document() *node { - n := p.node(documentNode) - n.anchors = make(map[string]*node) +func (p *parser) document() *Node { + n := p.node(DocumentNode, "", "", "") p.doc = n p.expect(yaml_DOCUMENT_START_EVENT) - n.children = append(n.children, p.parse()) + p.parseChild(n) + if p.peek() == yaml_DOCUMENT_END_EVENT { + n.FootComment = string(p.event.foot_comment) + } p.expect(yaml_DOCUMENT_END_EVENT) return n } -func (p *parser) alias() *node { - n := p.node(aliasNode) - n.value = string(p.event.anchor) - n.alias = p.doc.anchors[n.value] - if n.alias == nil { - failf("unknown anchor '%s' referenced", n.value) +func (p *parser) alias() *Node { + n := p.node(AliasNode, "", "", string(p.event.anchor)) + n.Alias = p.anchors[n.Value] + if n.Alias == nil { + failf("unknown anchor '%s' referenced", n.Value) } p.expect(yaml_ALIAS_EVENT) return n } -func (p *parser) scalar() *node { - n := p.node(scalarNode) - n.value = string(p.event.value) - n.tag = string(p.event.tag) - n.implicit = p.event.implicit +func (p *parser) scalar() *Node { + var parsedStyle = p.event.scalar_style() + var nodeStyle Style + switch { + case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: + nodeStyle = DoubleQuotedStyle + case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: + nodeStyle = SingleQuotedStyle + case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: + nodeStyle = LiteralStyle + case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: + nodeStyle = FoldedStyle + } + var nodeValue = string(p.event.value) + var nodeTag = string(p.event.tag) + var defaultTag string + if nodeStyle == 0 { + if nodeValue == "<<" { + defaultTag = mergeTag + } + } else { + defaultTag = strTag + } + n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) + n.Style |= nodeStyle p.anchor(n, p.event.anchor) p.expect(yaml_SCALAR_EVENT) return n } -func (p *parser) sequence() *node { - n := p.node(sequenceNode) +func (p *parser) sequence() *Node { + n := p.node(SequenceNode, seqTag, string(p.event.tag), "") + if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { + n.Style |= FlowStyle + } p.anchor(n, p.event.anchor) p.expect(yaml_SEQUENCE_START_EVENT) for p.peek() != yaml_SEQUENCE_END_EVENT { - n.children = append(n.children, p.parse()) + p.parseChild(n) } + n.LineComment = string(p.event.line_comment) + n.FootComment = string(p.event.foot_comment) p.expect(yaml_SEQUENCE_END_EVENT) return n } -func (p *parser) mapping() *node { - n := p.node(mappingNode) +func (p *parser) mapping() *Node { + n := p.node(MappingNode, mapTag, string(p.event.tag), "") + block := true + if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { + block = false + n.Style |= FlowStyle + } p.anchor(n, p.event.anchor) p.expect(yaml_MAPPING_START_EVENT) for p.peek() != yaml_MAPPING_END_EVENT { - n.children = append(n.children, p.parse(), p.parse()) + k := p.parseChild(n) + if block && k.FootComment != "" { + // Must be a foot comment for the prior value when being dedented. + if len(n.Content) > 2 { + n.Content[len(n.Content)-3].FootComment = k.FootComment + k.FootComment = "" + } + } + v := p.parseChild(n) + if k.FootComment == "" && v.FootComment != "" { + k.FootComment = v.FootComment + v.FootComment = "" + } + if p.peek() == yaml_TAIL_COMMENT_EVENT { + if k.FootComment == "" { + k.FootComment = string(p.event.foot_comment) + } + p.expect(yaml_TAIL_COMMENT_EVENT) + } + } + n.LineComment = string(p.event.line_comment) + n.FootComment = string(p.event.foot_comment) + if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { + n.Content[len(n.Content)-2].FootComment = n.FootComment + n.FootComment = "" } p.expect(yaml_MAPPING_END_EVENT) return n @@ -224,48 +311,70 @@ func (p *parser) mapping() *node { // Decoder, unmarshals a node into a provided value. type decoder struct { - doc *node - aliases map[*node]bool - mapType reflect.Type + doc *Node + aliases map[*Node]bool terrors []string - strict bool + stringMapType reflect.Type + generalMapType reflect.Type + + knownFields bool + uniqueKeys bool decodeCount int aliasCount int aliasDepth int + + mergedFields map[interface{}]bool } var ( - mapItemType = reflect.TypeOf(MapItem{}) + nodeType = reflect.TypeOf(Node{}) durationType = reflect.TypeOf(time.Duration(0)) - defaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) - ifaceType = defaultMapType.Elem() + stringMapType = reflect.TypeOf(map[string]interface{}{}) + generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) + ifaceType = generalMapType.Elem() timeType = reflect.TypeOf(time.Time{}) ptrTimeType = reflect.TypeOf(&time.Time{}) ) -func newDecoder(strict bool) *decoder { - d := &decoder{mapType: defaultMapType, strict: strict} - d.aliases = make(map[*node]bool) +func newDecoder() *decoder { + d := &decoder{ + stringMapType: stringMapType, + generalMapType: generalMapType, + uniqueKeys: true, + } + d.aliases = make(map[*Node]bool) return d } -func (d *decoder) terror(n *node, tag string, out reflect.Value) { - if n.tag != "" { - tag = n.tag +func (d *decoder) terror(n *Node, tag string, out reflect.Value) { + if n.Tag != "" { + tag = n.Tag } - value := n.value - if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG { + value := n.Value + if tag != seqTag && tag != mapTag { if len(value) > 10 { value = " `" + value[:7] + "...`" } else { value = " `" + value + "`" } } - d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type())) + d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) +} + +func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { + err := u.UnmarshalYAML(n) + if e, ok := err.(*TypeError); ok { + d.terrors = append(d.terrors, e.Errors...) + return false + } + if err != nil { + fail(err) + } + return true } -func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { +func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { terrlen := len(d.terrors) err := u.UnmarshalYAML(func(v interface{}) (err error) { defer handleErr(&err) @@ -294,8 +403,8 @@ func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { // its types unmarshalled appropriately. // // If n holds a null value, prepare returns before doing anything. -func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { - if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) { +func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { + if n.ShortTag() == nullTag { return out, false, false } again := true @@ -309,15 +418,40 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm again = true } if out.CanAddr() { - if u, ok := out.Addr().Interface().(Unmarshaler); ok { + outi := out.Addr().Interface() + if u, ok := outi.(Unmarshaler); ok { good = d.callUnmarshaler(n, u) return out, true, good } + if u, ok := outi.(obsoleteUnmarshaler); ok { + good = d.callObsoleteUnmarshaler(n, u) + return out, true, good + } } } return out, false, false } +func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { + if n.ShortTag() == nullTag { + return reflect.Value{} + } + for _, num := range index { + for { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + continue + } + break + } + v = v.Field(num) + } + return v +} + const ( // 400,000 decode operations is ~500kb of dense object declarations, or // ~5kb of dense object declarations with 10000% alias expansion @@ -347,7 +481,7 @@ func allowedAliasRatio(decodeCount int) float64 { } } -func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { +func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { d.decodeCount++ if d.aliasDepth > 0 { d.aliasCount++ @@ -355,46 +489,55 @@ func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { failf("document contains excessive aliasing") } - switch n.kind { - case documentNode: + if out.Type() == nodeType { + out.Set(reflect.ValueOf(n).Elem()) + return true + } + switch n.Kind { + case DocumentNode: return d.document(n, out) - case aliasNode: + case AliasNode: return d.alias(n, out) } out, unmarshaled, good := d.prepare(n, out) if unmarshaled { return good } - switch n.kind { - case scalarNode: + switch n.Kind { + case ScalarNode: good = d.scalar(n, out) - case mappingNode: + case MappingNode: good = d.mapping(n, out) - case sequenceNode: + case SequenceNode: good = d.sequence(n, out) + case 0: + if n.IsZero() { + return d.null(out) + } + fallthrough default: - panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) + failf("cannot decode node with unknown kind %d", n.Kind) } return good } -func (d *decoder) document(n *node, out reflect.Value) (good bool) { - if len(n.children) == 1 { +func (d *decoder) document(n *Node, out reflect.Value) (good bool) { + if len(n.Content) == 1 { d.doc = n - d.unmarshal(n.children[0], out) + d.unmarshal(n.Content[0], out) return true } return false } -func (d *decoder) alias(n *node, out reflect.Value) (good bool) { +func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { if d.aliases[n] { // TODO this could actually be allowed in some circumstances. - failf("anchor '%s' value contains itself", n.value) + failf("anchor '%s' value contains itself", n.Value) } d.aliases[n] = true d.aliasDepth++ - good = d.unmarshal(n.alias, out) + good = d.unmarshal(n.Alias, out) d.aliasDepth-- delete(d.aliases, n) return good @@ -408,15 +551,26 @@ func resetMap(out reflect.Value) { } } -func (d *decoder) scalar(n *node, out reflect.Value) bool { +func (d *decoder) null(out reflect.Value) bool { + if out.CanAddr() { + switch out.Kind() { + case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + out.Set(reflect.Zero(out.Type())) + return true + } + } + return false +} + +func (d *decoder) scalar(n *Node, out reflect.Value) bool { var tag string var resolved interface{} - if n.tag == "" && !n.implicit { - tag = yaml_STR_TAG - resolved = n.value + if n.indicatedString() { + tag = strTag + resolved = n.Value } else { - tag, resolved = resolve(n.tag, n.value) - if tag == yaml_BINARY_TAG { + tag, resolved = resolve(n.Tag, n.Value) + if tag == binaryTag { data, err := base64.StdEncoding.DecodeString(resolved.(string)) if err != nil { failf("!!binary value contains invalid base64 data") @@ -425,12 +579,7 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { } } if resolved == nil { - if out.Kind() == reflect.Map && !out.CanAddr() { - resetMap(out) - } else { - out.Set(reflect.Zero(out.Type())) - } - return true + return d.null(out) } if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { // We've resolved to exactly the type we want, so use that. @@ -443,13 +592,13 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) if ok { var text []byte - if tag == yaml_BINARY_TAG { + if tag == binaryTag { text = []byte(resolved.(string)) } else { // We let any value be unmarshaled into TextUnmarshaler. // That might be more lax than we'd like, but the // TextUnmarshaler itself should bowl out any dubious values. - text = []byte(n.value) + text = []byte(n.Value) } err := u.UnmarshalText(text) if err != nil { @@ -460,47 +609,37 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { } switch out.Kind() { case reflect.String: - if tag == yaml_BINARY_TAG { + if tag == binaryTag { out.SetString(resolved.(string)) return true } - if resolved != nil { - out.SetString(n.value) - return true - } + out.SetString(n.Value) + return true case reflect.Interface: - if resolved == nil { - out.Set(reflect.Zero(out.Type())) - } else if tag == yaml_TIMESTAMP_TAG { - // It looks like a timestamp but for backward compatibility - // reasons we set it as a string, so that code that unmarshals - // timestamp-like values into interface{} will continue to - // see a string and not a time.Time. - // TODO(v3) Drop this. - out.Set(reflect.ValueOf(n.value)) - } else { - out.Set(reflect.ValueOf(resolved)) - } + out.Set(reflect.ValueOf(resolved)) return true case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + // This used to work in v2, but it's very unfriendly. + isDuration := out.Type() == durationType + switch resolved := resolved.(type) { case int: - if !out.OverflowInt(int64(resolved)) { + if !isDuration && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) return true } case int64: - if !out.OverflowInt(resolved) { + if !isDuration && !out.OverflowInt(resolved) { out.SetInt(resolved) return true } case uint64: - if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) return true } case float64: - if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) return true } @@ -541,6 +680,17 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { case bool: out.SetBool(resolved) return true + case string: + // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). + // It only works if explicitly attempting to unmarshal into a typed bool value. + switch resolved { + case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": + out.SetBool(true) + return true + case "n", "N", "no", "No", "NO", "off", "Off", "OFF": + out.SetBool(false) + return true + } } case reflect.Float32, reflect.Float64: switch resolved := resolved.(type) { @@ -563,13 +713,7 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { return true } case reflect.Ptr: - if out.Type().Elem() == reflect.TypeOf(resolved) { - // TODO DOes this make sense? When is out a Ptr except when decoding a nil value? - elem := reflect.New(out.Type().Elem()) - elem.Elem().Set(reflect.ValueOf(resolved)) - out.Set(elem) - return true - } + panic("yaml internal error: please report the issue") } d.terror(n, tag, out) return false @@ -582,8 +726,8 @@ func settableValueOf(i interface{}) reflect.Value { return sv } -func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { - l := len(n.children) +func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { + l := len(n.Content) var iface reflect.Value switch out.Kind() { @@ -598,7 +742,7 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { iface = out out = settableValueOf(make([]interface{}, l)) default: - d.terror(n, yaml_SEQ_TAG, out) + d.terror(n, seqTag, out) return false } et := out.Type().Elem() @@ -606,7 +750,7 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { j := 0 for i := 0; i < l; i++ { e := reflect.New(et).Elem() - if ok := d.unmarshal(n.children[i], e); ok { + if ok := d.unmarshal(n.Content[i], e); ok { out.Index(j).Set(e) j++ } @@ -620,51 +764,79 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { return true } -func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { +func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { + l := len(n.Content) + if d.uniqueKeys { + nerrs := len(d.terrors) + for i := 0; i < l; i += 2 { + ni := n.Content[i] + for j := i + 2; j < l; j += 2 { + nj := n.Content[j] + if ni.Kind == nj.Kind && ni.Value == nj.Value { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) + } + } + } + if len(d.terrors) > nerrs { + return false + } + } switch out.Kind() { case reflect.Struct: return d.mappingStruct(n, out) - case reflect.Slice: - return d.mappingSlice(n, out) case reflect.Map: // okay case reflect.Interface: - if d.mapType.Kind() == reflect.Map { - iface := out - out = reflect.MakeMap(d.mapType) - iface.Set(out) + iface := out + if isStringMap(n) { + out = reflect.MakeMap(d.stringMapType) } else { - slicev := reflect.New(d.mapType).Elem() - if !d.mappingSlice(n, slicev) { - return false - } - out.Set(slicev) - return true + out = reflect.MakeMap(d.generalMapType) } + iface.Set(out) default: - d.terror(n, yaml_MAP_TAG, out) + d.terror(n, mapTag, out) return false } + outt := out.Type() kt := outt.Key() et := outt.Elem() - mapType := d.mapType - if outt.Key() == ifaceType && outt.Elem() == ifaceType { - d.mapType = outt + stringMapType := d.stringMapType + generalMapType := d.generalMapType + if outt.Elem() == ifaceType { + if outt.Key().Kind() == reflect.String { + d.stringMapType = outt + } else if outt.Key() == ifaceType { + d.generalMapType = outt + } } + mergedFields := d.mergedFields + d.mergedFields = nil + + var mergeNode *Node + + mapIsNew := false if out.IsNil() { out.Set(reflect.MakeMap(outt)) + mapIsNew = true } - l := len(n.children) for i := 0; i < l; i += 2 { - if isMerge(n.children[i]) { - d.merge(n.children[i+1], out) + if isMerge(n.Content[i]) { + mergeNode = n.Content[i+1] continue } k := reflect.New(kt).Elem() - if d.unmarshal(n.children[i], k) { + if d.unmarshal(n.Content[i], k) { + if mergedFields != nil { + ki := k.Interface() + if d.getPossiblyUnhashableKey(mergedFields, ki) { + continue + } + d.setPossiblyUnhashableKey(mergedFields, ki, true) + } kkind := k.Kind() if kkind == reflect.Interface { kkind = k.Elem().Kind() @@ -673,87 +845,83 @@ func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { failf("invalid map key: %#v", k.Interface()) } e := reflect.New(et).Elem() - if d.unmarshal(n.children[i+1], e) { - d.setMapIndex(n.children[i+1], out, k, e) + if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) { + out.SetMapIndex(k, e) } } } - d.mapType = mapType - return true -} -func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) { - if d.strict && out.MapIndex(k) != zeroValue { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface())) - return + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) } - out.SetMapIndex(k, v) + + d.stringMapType = stringMapType + d.generalMapType = generalMapType + return true } -func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { - outt := out.Type() - if outt.Elem() != mapItemType { - d.terror(n, yaml_MAP_TAG, out) +func isStringMap(n *Node) bool { + if n.Kind != MappingNode { return false } - - mapType := d.mapType - d.mapType = outt - - var slice []MapItem - var l = len(n.children) + l := len(n.Content) for i := 0; i < l; i += 2 { - if isMerge(n.children[i]) { - d.merge(n.children[i+1], out) - continue - } - item := MapItem{} - k := reflect.ValueOf(&item.Key).Elem() - if d.unmarshal(n.children[i], k) { - v := reflect.ValueOf(&item.Value).Elem() - if d.unmarshal(n.children[i+1], v) { - slice = append(slice, item) - } + shortTag := n.Content[i].ShortTag() + if shortTag != strTag && shortTag != mergeTag { + return false } } - out.Set(reflect.ValueOf(slice)) - d.mapType = mapType return true } -func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { +func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { sinfo, err := getStructInfo(out.Type()) if err != nil { panic(err) } - name := settableValueOf("") - l := len(n.children) var inlineMap reflect.Value var elemType reflect.Type if sinfo.InlineMap != -1 { inlineMap = out.Field(sinfo.InlineMap) - inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) elemType = inlineMap.Type().Elem() } + for _, index := range sinfo.InlineUnmarshalers { + field := d.fieldByIndex(n, out, index) + d.prepare(n, field) + } + + mergedFields := d.mergedFields + d.mergedFields = nil + var mergeNode *Node var doneFields []bool - if d.strict { + if d.uniqueKeys { doneFields = make([]bool, len(sinfo.FieldsList)) } + name := settableValueOf("") + l := len(n.Content) for i := 0; i < l; i += 2 { - ni := n.children[i] + ni := n.Content[i] if isMerge(ni) { - d.merge(n.children[i+1], out) + mergeNode = n.Content[i+1] continue } if !d.unmarshal(ni, name) { continue } - if info, ok := sinfo.FieldsMap[name.String()]; ok { - if d.strict { + sname := name.String() + if mergedFields != nil { + if mergedFields[sname] { + continue + } + mergedFields[sname] = true + } + if info, ok := sinfo.FieldsMap[sname]; ok { + if d.uniqueKeys { if doneFields[info.Id] { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type())) + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) continue } doneFields[info.Id] = true @@ -762,20 +930,25 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { if info.Inline == nil { field = out.Field(info.Num) } else { - field = out.FieldByIndex(info.Inline) + field = d.fieldByIndex(n, out, info.Inline) } - d.unmarshal(n.children[i+1], field) + d.unmarshal(n.Content[i+1], field) } else if sinfo.InlineMap != -1 { if inlineMap.IsNil() { inlineMap.Set(reflect.MakeMap(inlineMap.Type())) } value := reflect.New(elemType).Elem() - d.unmarshal(n.children[i+1], value) - d.setMapIndex(n.children[i+1], inlineMap, name, value) - } else if d.strict { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type())) + d.unmarshal(n.Content[i+1], value) + inlineMap.SetMapIndex(name, value) + } else if d.knownFields { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) } } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } return true } @@ -783,24 +956,52 @@ func failWantMap() { failf("map merge requires map or sequence of maps as the value") } -func (d *decoder) merge(n *node, out reflect.Value) { - switch n.kind { - case mappingNode: - d.unmarshal(n, out) - case aliasNode: - if n.alias != nil && n.alias.kind != mappingNode { +func (d *decoder) setPossiblyUnhashableKey(m map[interface{}]bool, key interface{}, value bool) { + defer func() { + if err := recover(); err != nil { + failf("%v", err) + } + }() + m[key] = value +} + +func (d *decoder) getPossiblyUnhashableKey(m map[interface{}]bool, key interface{}) bool { + defer func() { + if err := recover(); err != nil { + failf("%v", err) + } + }() + return m[key] +} + +func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { + mergedFields := d.mergedFields + if mergedFields == nil { + d.mergedFields = make(map[interface{}]bool) + for i := 0; i < len(parent.Content); i += 2 { + k := reflect.New(ifaceType).Elem() + if d.unmarshal(parent.Content[i], k) { + d.setPossiblyUnhashableKey(d.mergedFields, k.Interface(), true) + } + } + } + + switch merge.Kind { + case MappingNode: + d.unmarshal(merge, out) + case AliasNode: + if merge.Alias != nil && merge.Alias.Kind != MappingNode { failWantMap() } - d.unmarshal(n, out) - case sequenceNode: - // Step backwards as earlier nodes take precedence. - for i := len(n.children) - 1; i >= 0; i-- { - ni := n.children[i] - if ni.kind == aliasNode { - if ni.alias != nil && ni.alias.kind != mappingNode { + d.unmarshal(merge, out) + case SequenceNode: + for i := 0; i < len(merge.Content); i++ { + ni := merge.Content[i] + if ni.Kind == AliasNode { + if ni.Alias != nil && ni.Alias.Kind != MappingNode { failWantMap() } - } else if ni.kind != mappingNode { + } else if ni.Kind != MappingNode { failWantMap() } d.unmarshal(ni, out) @@ -808,8 +1009,10 @@ func (d *decoder) merge(n *node, out reflect.Value) { default: failWantMap() } + + d.mergedFields = mergedFields } -func isMerge(n *node) bool { - return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) +func isMerge(n *Node) bool { + return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) } diff --git a/vendor/gopkg.in/yaml.v2/emitterc.go b/vendor/go.yaml.in/yaml/v3/emitterc.go similarity index 76% rename from vendor/gopkg.in/yaml.v2/emitterc.go rename to vendor/go.yaml.in/yaml/v3/emitterc.go index a1c2cc526..ab4e03ba7 100644 --- a/vendor/gopkg.in/yaml.v2/emitterc.go +++ b/vendor/go.yaml.in/yaml/v3/emitterc.go @@ -1,3 +1,25 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + package yaml import ( @@ -43,8 +65,13 @@ func put_break(emitter *yaml_emitter_t) bool { default: panic("unknown line break setting") } + if emitter.column == 0 { + emitter.space_above = true + } emitter.column = 0 emitter.line++ + // [Go] Do this here and below and drop from everywhere else (see commented lines). + emitter.indention = true return true } @@ -97,8 +124,13 @@ func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { if !write(emitter, s, i) { return false } + if emitter.column == 0 { + emitter.space_above = true + } emitter.column = 0 emitter.line++ + // [Go] Do this here and above and drop from everywhere else (see commented lines). + emitter.indention = true } return true } @@ -130,10 +162,9 @@ func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { // Check if we need to accumulate more events before emitting. // // We accumulate extra -// - 1 event for DOCUMENT-START -// - 2 events for SEQUENCE-START -// - 3 events for MAPPING-START -// +// - 1 event for DOCUMENT-START +// - 2 events for SEQUENCE-START +// - 3 events for MAPPING-START func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { if emitter.events_head == len(emitter.events) { return true @@ -194,7 +225,7 @@ func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_ } // Increase the indentation level. -func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { +func yaml_emitter_increase_indent_compact(emitter *yaml_emitter_t, flow, indentless bool, compact_seq bool) bool { emitter.indents = append(emitter.indents, emitter.indent) if emitter.indent < 0 { if flow { @@ -203,7 +234,21 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool emitter.indent = 0 } } else if !indentless { - emitter.indent += emitter.best_indent + // [Go] This was changed so that indentations are more regular. + if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE { + // The first indent inside a sequence will just skip the "- " indicator. + emitter.indent += 2 + } else { + // Everything else aligns to the chosen indentation. + emitter.indent = emitter.best_indent * ((emitter.indent + emitter.best_indent) / emitter.best_indent) + if compact_seq { + // The value compact_seq passed in is almost always set to `false` when this function is called, + // except when we are dealing with sequence nodes. So this gets triggered to subtract 2 only when we + // are increasing the indent to account for sequence nodes, which will be correct because we need to + // subtract 2 to account for the - at the beginning of the sequence node. + emitter.indent = emitter.indent - 2 + } + } } return true } @@ -228,16 +273,22 @@ func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bo return yaml_emitter_emit_document_end(emitter, event) case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: - return yaml_emitter_emit_flow_sequence_item(emitter, event, true) + return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false) + + case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true) case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: - return yaml_emitter_emit_flow_sequence_item(emitter, event, false) + return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false) case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: - return yaml_emitter_emit_flow_mapping_key(emitter, event, true) + return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false) + + case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true) case yaml_EMIT_FLOW_MAPPING_KEY_STATE: - return yaml_emitter_emit_flow_mapping_key(emitter, event, false) + return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false) case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: return yaml_emitter_emit_flow_mapping_value(emitter, event, true) @@ -298,6 +349,8 @@ func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t emitter.column = 0 emitter.whitespace = true emitter.indention = true + emitter.space_above = true + emitter.foot_indent = -1 if emitter.encoding != yaml_UTF8_ENCODING { if !yaml_emitter_write_bom(emitter) { @@ -392,13 +445,22 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { return false } - if emitter.canonical { + if emitter.canonical || true { if !yaml_emitter_write_indent(emitter) { return false } } } + if len(emitter.head_comment) > 0 { + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if !put_break(emitter) { + return false + } + } + emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE return true } @@ -422,10 +484,35 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") } +// yaml_emitter_increase_indent preserves the original signature and delegates to +// yaml_emitter_increase_indent_compact without compact-sequence indentation +func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { + return yaml_emitter_increase_indent_compact(emitter, flow, indentless, false) +} + +// yaml_emitter_process_line_comment preserves the original signature and delegates to +// yaml_emitter_process_line_comment_linebreak passing false for linebreak +func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool { + return yaml_emitter_process_line_comment_linebreak(emitter, false) +} + // Expect the root node. func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) - return yaml_emitter_emit_node(emitter, event, true, false, false, false) + + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if !yaml_emitter_emit_node(emitter, event, true, false, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true } // Expect DOCUMENT-END. @@ -433,6 +520,12 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t if event.typ != yaml_DOCUMENT_END_EVENT { return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") } + // [Go] Force document foot separation. + emitter.foot_indent = 0 + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + emitter.foot_indent = -1 if !yaml_emitter_write_indent(emitter) { return false } @@ -454,7 +547,7 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t } // Expect a flow item node. -func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { +func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { if first { if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { return false @@ -466,13 +559,15 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e } if event.typ == yaml_SEQUENCE_END_EVENT { - emitter.flow_level-- - emitter.indent = emitter.indents[len(emitter.indents)-1] - emitter.indents = emitter.indents[:len(emitter.indents)-1] - if emitter.canonical && !first { + if emitter.canonical && !first && !trail { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } + } + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.column == 0 || emitter.canonical && !first { if !yaml_emitter_write_indent(emitter) { return false } @@ -480,29 +575,62 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { return false } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } - if !first { + if !first && !trail { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if emitter.column == 0 { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } - emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) - return yaml_emitter_emit_node(emitter, event, false, true, false, false) + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE) + } else { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) + } + if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { + return false + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true } // Expect a flow key node. -func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { +func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { if first { if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { return false @@ -514,13 +642,18 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve } if event.typ == yaml_MAPPING_END_EVENT { + if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if !yaml_emitter_process_head_comment(emitter) { + return false + } emitter.flow_level-- emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] if emitter.canonical && !first { - if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { - return false - } if !yaml_emitter_write_indent(emitter) { return false } @@ -528,16 +661,33 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { return false } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } - if !first { + if !first && !trail { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } + + if !yaml_emitter_process_head_comment(emitter) { + return false + } + + if emitter.column == 0 { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false @@ -571,14 +721,41 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e return false } } - emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) - return yaml_emitter_emit_node(emitter, event, false, false, true, false) + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE) + } else { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) + } + if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { + return false + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true } // Expect a block item node. func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { - if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) { + // emitter.mapping context tells us if we are currently in a mapping context. + // emiiter.column tells us which column we are in in the yaml output. 0 is the first char of the column. + // emitter.indentation tells us if the last character was an indentation character. + // emitter.compact_sequence_indent tells us if '- ' is considered part of the indentation for sequence elements. + // So, `seq` means that we are in a mapping context, and we are either at the first char of the column or + // the last character was not an indentation character, and we consider '- ' part of the indentation + // for sequence elements. + seq := emitter.mapping_context && (emitter.column == 0 || !emitter.indention) && + emitter.compact_sequence_indent + if !yaml_emitter_increase_indent_compact(emitter, false, false, seq) { return false } } @@ -589,6 +766,9 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_ emitter.states = emitter.states[:len(emitter.states)-1] return true } + if !yaml_emitter_process_head_comment(emitter) { + return false + } if !yaml_emitter_write_indent(emitter) { return false } @@ -596,7 +776,16 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_ return false } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) - return yaml_emitter_emit_node(emitter, event, false, true, false, false) + if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true } // Expect a block key node. @@ -606,6 +795,9 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev return false } } + if !yaml_emitter_process_head_comment(emitter) { + return false + } if event.typ == yaml_MAPPING_END_EVENT { emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] @@ -616,6 +808,13 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev if !yaml_emitter_write_indent(emitter) { return false } + if len(emitter.line_comment) > 0 { + // [Go] A line comment was provided for the key. That's unusual as the + // scanner associates line comments with the value. Either way, + // save the line comment and render it appropriately later. + emitter.key_line_comment = emitter.line_comment + emitter.line_comment = nil + } if yaml_emitter_check_simple_key(emitter) { emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, true) @@ -641,8 +840,42 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_ return false } } + if len(emitter.key_line_comment) > 0 { + // [Go] Line comments are generally associated with the value, but when there's + // no value on the same line as a mapping key they end up attached to the + // key itself. + if event.typ == yaml_SCALAR_EVENT { + if len(emitter.line_comment) == 0 { + // A scalar is coming and it has no line comments by itself yet, + // so just let it handle the line comment as usual. If it has a + // line comment, we can't have both so the one from the key is lost. + emitter.line_comment = emitter.key_line_comment + emitter.key_line_comment = nil + } + } else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) { + // An indented block follows, so write the comment right now. + emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment + if !yaml_emitter_process_line_comment(emitter) { + return false + } + emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment + } + } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) - return yaml_emitter_emit_node(emitter, event, false, false, true, false) + if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { + return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0 } // Expect a node. @@ -908,6 +1141,78 @@ func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { panic("unknown scalar style") } +// Write a head comment. +func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool { + if len(emitter.tail_comment) > 0 { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_comment(emitter, emitter.tail_comment) { + return false + } + emitter.tail_comment = emitter.tail_comment[:0] + emitter.foot_indent = emitter.indent + if emitter.foot_indent < 0 { + emitter.foot_indent = 0 + } + } + + if len(emitter.head_comment) == 0 { + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_comment(emitter, emitter.head_comment) { + return false + } + emitter.head_comment = emitter.head_comment[:0] + return true +} + +// Write an line comment. +func yaml_emitter_process_line_comment_linebreak(emitter *yaml_emitter_t, linebreak bool) bool { + if len(emitter.line_comment) == 0 { + // The next 3 lines are needed to resolve an issue with leading newlines + // See https://github.com/go-yaml/yaml/issues/755 + // When linebreak is set to true, put_break will be called and will add + // the needed newline. + if linebreak && !put_break(emitter) { + return false + } + return true + } + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !yaml_emitter_write_comment(emitter, emitter.line_comment) { + return false + } + emitter.line_comment = emitter.line_comment[:0] + return true +} + +// Write a foot comment. +func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool { + if len(emitter.foot_comment) == 0 { + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_comment(emitter, emitter.foot_comment) { + return false + } + emitter.foot_comment = emitter.foot_comment[:0] + emitter.foot_indent = emitter.indent + if emitter.foot_indent < 0 { + emitter.foot_indent = 0 + } + return true +} + // Check if a %YAML directive is valid. func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { if version_directive.major != 1 || version_directive.minor != 1 { @@ -987,6 +1292,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { flow_indicators = false line_breaks = false special_characters = false + tab_characters = false leading_space = false leading_break = false @@ -1055,7 +1361,9 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { } } - if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { + if value[i] == '\t' { + tab_characters = true + } else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { special_characters = true } if is_space(value, i) { @@ -1110,10 +1418,12 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { emitter.scalar_data.block_plain_allowed = false emitter.scalar_data.single_quoted_allowed = false } - if space_break || special_characters { + if space_break || tab_characters || special_characters { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false emitter.scalar_data.single_quoted_allowed = false + } + if space_break || special_characters { emitter.scalar_data.block_allowed = false } if line_breaks { @@ -1137,6 +1447,19 @@ func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bo emitter.tag_data.suffix = nil emitter.scalar_data.value = nil + if len(event.head_comment) > 0 { + emitter.head_comment = event.head_comment + } + if len(event.line_comment) > 0 { + emitter.line_comment = event.line_comment + } + if len(event.foot_comment) > 0 { + emitter.foot_comment = event.foot_comment + } + if len(event.tail_comment) > 0 { + emitter.tail_comment = event.tail_comment + } + switch event.typ { case yaml_ALIAS_EVENT: if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { @@ -1208,13 +1531,20 @@ func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { return false } } + if emitter.foot_indent == indent { + if !put_break(emitter) { + return false + } + } for emitter.column < indent { if !put(emitter, ' ') { return false } } emitter.whitespace = true - emitter.indention = true + //emitter.indention = true + emitter.space_above = false + emitter.foot_indent = -1 return true } @@ -1311,7 +1641,7 @@ func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_ } func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { - if !emitter.whitespace { + if len(value) > 0 && !emitter.whitespace { if !put(emitter, ' ') { return false } @@ -1341,7 +1671,7 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo if !write_break(emitter, value, &i) { return false } - emitter.indention = true + //emitter.indention = true breaks = true } else { if breaks { @@ -1358,7 +1688,9 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo } } - emitter.whitespace = false + if len(value) > 0 { + emitter.whitespace = false + } emitter.indention = false if emitter.root_context { emitter.open_ended = true @@ -1397,7 +1729,7 @@ func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []by if !write_break(emitter, value, &i) { return false } - emitter.indention = true + //emitter.indention = true breaks = true } else { if breaks { @@ -1596,10 +1928,10 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } - if !put_break(emitter) { + if !yaml_emitter_process_line_comment_linebreak(emitter, true) { return false } - emitter.indention = true + //emitter.indention = true emitter.whitespace = true breaks := true for i := 0; i < len(value); { @@ -1607,7 +1939,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo if !write_break(emitter, value, &i) { return false } - emitter.indention = true + //emitter.indention = true breaks = true } else { if breaks { @@ -1633,11 +1965,11 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } - - if !put_break(emitter) { + if !yaml_emitter_process_line_comment_linebreak(emitter, true) { return false } - emitter.indention = true + + //emitter.indention = true emitter.whitespace = true breaks := true @@ -1658,7 +1990,7 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo if !write_break(emitter, value, &i) { return false } - emitter.indention = true + //emitter.indention = true breaks = true } else { if breaks { @@ -1683,3 +2015,40 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo } return true } + +func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool { + breaks := false + pound := false + for i := 0; i < len(comment); { + if is_break(comment, i) { + if !write_break(emitter, comment, &i) { + return false + } + //emitter.indention = true + breaks = true + pound = false + } else { + if breaks && !yaml_emitter_write_indent(emitter) { + return false + } + if !pound { + if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) { + return false + } + pound = true + } + if !write(emitter, comment, &i) { + return false + } + emitter.indention = false + breaks = false + } + } + if !breaks && !put_break(emitter) { + return false + } + + emitter.whitespace = true + //emitter.indention = true + return true +} diff --git a/vendor/go.yaml.in/yaml/v3/encode.go b/vendor/go.yaml.in/yaml/v3/encode.go new file mode 100644 index 000000000..de9e72a3e --- /dev/null +++ b/vendor/go.yaml.in/yaml/v3/encode.go @@ -0,0 +1,577 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yaml + +import ( + "encoding" + "fmt" + "io" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +type encoder struct { + emitter yaml_emitter_t + event yaml_event_t + out []byte + flow bool + indent int + doneInit bool +} + +func newEncoder() *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_string(&e.emitter, &e.out) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func newEncoderWithWriter(w io.Writer) *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_writer(&e.emitter, w) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func (e *encoder) init() { + if e.doneInit { + return + } + if e.indent == 0 { + e.indent = 4 + } + e.emitter.best_indent = e.indent + yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) + e.emit() + e.doneInit = true +} + +func (e *encoder) finish() { + e.emitter.open_ended = false + yaml_stream_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) destroy() { + yaml_emitter_delete(&e.emitter) +} + +func (e *encoder) emit() { + // This will internally delete the e.event value. + e.must(yaml_emitter_emit(&e.emitter, &e.event)) +} + +func (e *encoder) must(ok bool) { + if !ok { + msg := e.emitter.problem + if msg == "" { + msg = "unknown problem generating YAML content" + } + failf("%s", msg) + } +} + +func (e *encoder) marshalDoc(tag string, in reflect.Value) { + e.init() + var node *Node + if in.IsValid() { + node, _ = in.Interface().(*Node) + } + if node != nil && node.Kind == DocumentNode { + e.nodev(in) + } else { + yaml_document_start_event_initialize(&e.event, nil, nil, true) + e.emit() + e.marshal(tag, in) + yaml_document_end_event_initialize(&e.event, true) + e.emit() + } +} + +func (e *encoder) marshal(tag string, in reflect.Value) { + tag = shortTag(tag) + if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { + e.nilv() + return + } + iface := in.Interface() + switch value := iface.(type) { + case *Node: + e.nodev(in) + return + case Node: + if !in.CanAddr() { + var n = reflect.New(in.Type()).Elem() + n.Set(in) + in = n + } + e.nodev(in.Addr()) + return + case time.Time: + e.timev(tag, in) + return + case *time.Time: + e.timev(tag, in.Elem()) + return + case time.Duration: + e.stringv(tag, reflect.ValueOf(value.String())) + return + case Marshaler: + v, err := value.MarshalYAML() + if err != nil { + fail(err) + } + if v == nil { + e.nilv() + return + } + e.marshal(tag, reflect.ValueOf(v)) + return + case encoding.TextMarshaler: + text, err := value.MarshalText() + if err != nil { + fail(err) + } + in = reflect.ValueOf(string(text)) + case nil: + e.nilv() + return + } + switch in.Kind() { + case reflect.Interface: + e.marshal(tag, in.Elem()) + case reflect.Map: + e.mapv(tag, in) + case reflect.Ptr: + e.marshal(tag, in.Elem()) + case reflect.Struct: + e.structv(tag, in) + case reflect.Slice, reflect.Array: + e.slicev(tag, in) + case reflect.String: + e.stringv(tag, in) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + e.intv(tag, in) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + e.uintv(tag, in) + case reflect.Float32, reflect.Float64: + e.floatv(tag, in) + case reflect.Bool: + e.boolv(tag, in) + default: + panic("cannot marshal type: " + in.Type().String()) + } +} + +func (e *encoder) mapv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + keys := keyList(in.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + e.marshal("", k) + e.marshal("", in.MapIndex(k)) + } + }) +} + +func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { + for _, num := range index { + for { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return reflect.Value{} + } + v = v.Elem() + continue + } + break + } + v = v.Field(num) + } + return v +} + +func (e *encoder) structv(tag string, in reflect.Value) { + sinfo, err := getStructInfo(in.Type()) + if err != nil { + panic(err) + } + e.mappingv(tag, func() { + for _, info := range sinfo.FieldsList { + var value reflect.Value + if info.Inline == nil { + value = in.Field(info.Num) + } else { + value = e.fieldByIndex(in, info.Inline) + if !value.IsValid() { + continue + } + } + if info.OmitEmpty && isZero(value) { + continue + } + e.marshal("", reflect.ValueOf(info.Key)) + e.flow = info.Flow + e.marshal("", value) + } + if sinfo.InlineMap >= 0 { + m := in.Field(sinfo.InlineMap) + if m.Len() > 0 { + e.flow = false + keys := keyList(m.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + if _, found := sinfo.FieldsMap[k.String()]; found { + panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) + } + e.marshal("", k) + e.flow = false + e.marshal("", m.MapIndex(k)) + } + } + } + }) +} + +func (e *encoder) mappingv(tag string, f func()) { + implicit := tag == "" + style := yaml_BLOCK_MAPPING_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_MAPPING_STYLE + } + yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) + e.emit() + f() + yaml_mapping_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) slicev(tag string, in reflect.Value) { + implicit := tag == "" + style := yaml_BLOCK_SEQUENCE_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) + e.emit() + n := in.Len() + for i := 0; i < n; i++ { + e.marshal("", in.Index(i)) + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.emit() +} + +// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. +// +// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported +// in YAML 1.2 and by this package, but these should be marshalled quoted for +// the time being for compatibility with other parsers. +func isBase60Float(s string) (result bool) { + // Fast path. + if s == "" { + return false + } + c := s[0] + if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { + return false + } + // Do the full match. + return base60float.MatchString(s) +} + +// From http://yaml.org/type/float.html, except the regular expression there +// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. +var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) + +// isOldBool returns whether s is bool notation as defined in YAML 1.1. +// +// We continue to force strings that YAML 1.1 would interpret as booleans to be +// rendered as quotes strings so that the marshalled output valid for YAML 1.1 +// parsing. +func isOldBool(s string) (result bool) { + switch s { + case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", + "n", "N", "no", "No", "NO", "off", "Off", "OFF": + return true + default: + return false + } +} + +func (e *encoder) stringv(tag string, in reflect.Value) { + var style yaml_scalar_style_t + s := in.String() + canUsePlain := true + switch { + case !utf8.ValidString(s): + if tag == binaryTag { + failf("explicitly tagged !!binary data must be base64-encoded") + } + if tag != "" { + failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) + } + // It can't be encoded directly as YAML so use a binary tag + // and encode it as base64. + tag = binaryTag + s = encodeBase64(s) + case tag == "": + // Check to see if it would resolve to a specific + // tag when encoded unquoted. If it doesn't, + // there's no need to quote it. + rtag, _ := resolve("", s) + canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) + } + // Note: it's possible for user code to emit invalid YAML + // if they explicitly specify a tag and a string containing + // text that's incompatible with that tag. + switch { + case strings.Contains(s, "\n"): + if e.flow { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } else { + style = yaml_LITERAL_SCALAR_STYLE + } + case canUsePlain: + style = yaml_PLAIN_SCALAR_STYLE + default: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + e.emitScalar(s, "", tag, style, nil, nil, nil, nil) +} + +func (e *encoder) boolv(tag string, in reflect.Value) { + var s string + if in.Bool() { + s = "true" + } else { + s = "false" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) intv(tag string, in reflect.Value) { + s := strconv.FormatInt(in.Int(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) uintv(tag string, in reflect.Value) { + s := strconv.FormatUint(in.Uint(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) timev(tag string, in reflect.Value) { + t := in.Interface().(time.Time) + s := t.Format(time.RFC3339Nano) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) floatv(tag string, in reflect.Value) { + // Issue #352: When formatting, use the precision of the underlying value + precision := 64 + if in.Kind() == reflect.Float32 { + precision = 32 + } + + s := strconv.FormatFloat(in.Float(), 'g', -1, precision) + switch s { + case "+Inf": + s = ".inf" + case "-Inf": + s = "-.inf" + case "NaN": + s = ".nan" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) nilv() { + e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { + // TODO Kill this function. Replace all initialize calls by their underlining Go literals. + implicit := tag == "" + if !implicit { + tag = longTag(tag) + } + e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) + e.event.head_comment = head + e.event.line_comment = line + e.event.foot_comment = foot + e.event.tail_comment = tail + e.emit() +} + +func (e *encoder) nodev(in reflect.Value) { + e.node(in.Interface().(*Node), "") +} + +func (e *encoder) node(node *Node, tail string) { + // Zero nodes behave as nil. + if node.Kind == 0 && node.IsZero() { + e.nilv() + return + } + + // If the tag was not explicitly requested, and dropping it won't change the + // implicit tag of the value, don't include it in the presentation. + var tag = node.Tag + var stag = shortTag(tag) + var forceQuoting bool + if tag != "" && node.Style&TaggedStyle == 0 { + if node.Kind == ScalarNode { + if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { + tag = "" + } else { + rtag, _ := resolve("", node.Value) + if rtag == stag { + tag = "" + } else if stag == strTag { + tag = "" + forceQuoting = true + } + } + } else { + var rtag string + switch node.Kind { + case MappingNode: + rtag = mapTag + case SequenceNode: + rtag = seqTag + } + if rtag == stag { + tag = "" + } + } + } + + switch node.Kind { + case DocumentNode: + yaml_document_start_event_initialize(&e.event, nil, nil, true) + e.event.head_comment = []byte(node.HeadComment) + e.emit() + for _, node := range node.Content { + e.node(node, "") + } + yaml_document_end_event_initialize(&e.event, true) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case SequenceNode: + style := yaml_BLOCK_SEQUENCE_STYLE + if node.Style&FlowStyle != 0 { + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)) + e.event.head_comment = []byte(node.HeadComment) + e.emit() + for _, node := range node.Content { + e.node(node, "") + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.event.line_comment = []byte(node.LineComment) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case MappingNode: + style := yaml_BLOCK_MAPPING_STYLE + if node.Style&FlowStyle != 0 { + style = yaml_FLOW_MAPPING_STYLE + } + yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style) + e.event.tail_comment = []byte(tail) + e.event.head_comment = []byte(node.HeadComment) + e.emit() + + // The tail logic below moves the foot comment of prior keys to the following key, + // since the value for each key may be a nested structure and the foot needs to be + // processed only the entirety of the value is streamed. The last tail is processed + // with the mapping end event. + var tail string + for i := 0; i+1 < len(node.Content); i += 2 { + k := node.Content[i] + foot := k.FootComment + if foot != "" { + kopy := *k + kopy.FootComment = "" + k = &kopy + } + e.node(k, tail) + tail = foot + + v := node.Content[i+1] + e.node(v, "") + } + + yaml_mapping_end_event_initialize(&e.event) + e.event.tail_comment = []byte(tail) + e.event.line_comment = []byte(node.LineComment) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case AliasNode: + yaml_alias_event_initialize(&e.event, []byte(node.Value)) + e.event.head_comment = []byte(node.HeadComment) + e.event.line_comment = []byte(node.LineComment) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case ScalarNode: + value := node.Value + if !utf8.ValidString(value) { + if stag == binaryTag { + failf("explicitly tagged !!binary data must be base64-encoded") + } + if stag != "" { + failf("cannot marshal invalid UTF-8 data as %s", stag) + } + // It can't be encoded directly as YAML so use a binary tag + // and encode it as base64. + tag = binaryTag + value = encodeBase64(value) + } + + style := yaml_PLAIN_SCALAR_STYLE + switch { + case node.Style&DoubleQuotedStyle != 0: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + case node.Style&SingleQuotedStyle != 0: + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + case node.Style&LiteralStyle != 0: + style = yaml_LITERAL_SCALAR_STYLE + case node.Style&FoldedStyle != 0: + style = yaml_FOLDED_SCALAR_STYLE + case strings.Contains(value, "\n"): + style = yaml_LITERAL_SCALAR_STYLE + case forceQuoting: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + + e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) + default: + failf("cannot encode node with unknown kind %d", node.Kind) + } +} diff --git a/vendor/gopkg.in/yaml.v2/parserc.go b/vendor/go.yaml.in/yaml/v3/parserc.go similarity index 81% rename from vendor/gopkg.in/yaml.v2/parserc.go rename to vendor/go.yaml.in/yaml/v3/parserc.go index 81d05dfe5..25fe82363 100644 --- a/vendor/gopkg.in/yaml.v2/parserc.go +++ b/vendor/go.yaml.in/yaml/v3/parserc.go @@ -1,3 +1,25 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + package yaml import ( @@ -45,11 +67,46 @@ import ( // Peek the next token in the token queue. func peek_token(parser *yaml_parser_t) *yaml_token_t { if parser.token_available || yaml_parser_fetch_more_tokens(parser) { - return &parser.tokens[parser.tokens_head] + token := &parser.tokens[parser.tokens_head] + yaml_parser_unfold_comments(parser, token) + return token } return nil } +// yaml_parser_unfold_comments walks through the comments queue and joins all +// comments behind the position of the provided token into the respective +// top-level comment slices in the parser. +func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) { + for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index { + comment := &parser.comments[parser.comments_head] + if len(comment.head) > 0 { + if token.typ == yaml_BLOCK_END_TOKEN { + // No heads on ends, so keep comment.head for a follow up token. + break + } + if len(parser.head_comment) > 0 { + parser.head_comment = append(parser.head_comment, '\n') + } + parser.head_comment = append(parser.head_comment, comment.head...) + } + if len(comment.foot) > 0 { + if len(parser.foot_comment) > 0 { + parser.foot_comment = append(parser.foot_comment, '\n') + } + parser.foot_comment = append(parser.foot_comment, comment.foot...) + } + if len(comment.line) > 0 { + if len(parser.line_comment) > 0 { + parser.line_comment = append(parser.line_comment, '\n') + } + parser.line_comment = append(parser.line_comment, comment.line...) + } + *comment = yaml_comment_t{} + parser.comments_head++ + } +} + // Remove the next token from the queue (must be called after peek_token). func skip_token(parser *yaml_parser_t) { parser.token_available = false @@ -170,7 +227,8 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool // Parse the production: // stream ::= STREAM-START implicit_document? explicit_document* STREAM-END -// ************ +// +// ************ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -192,9 +250,12 @@ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* -// * +// +// * +// // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -// ************************* +// +// ************************* func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { token := peek_token(parser) @@ -224,10 +285,32 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) parser.state = yaml_PARSE_BLOCK_NODE_STATE + var head_comment []byte + if len(parser.head_comment) > 0 { + // [Go] Scan the header comment backwards, and if an empty line is found, break + // the header so the part before the last empty line goes into the + // document header, while the bottom of it goes into a follow up event. + for i := len(parser.head_comment) - 1; i > 0; i-- { + if parser.head_comment[i] == '\n' { + if i == len(parser.head_comment)-1 { + head_comment = parser.head_comment[:i] + parser.head_comment = parser.head_comment[i+1:] + break + } else if parser.head_comment[i-1] == '\n' { + head_comment = parser.head_comment[:i-1] + parser.head_comment = parser.head_comment[i+1:] + break + } + } + } + } + *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, + + head_comment: head_comment, } } else if token.typ != yaml_STREAM_END_TOKEN { @@ -277,13 +360,14 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t // Parse the productions: // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -// *********** // +// *********** func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN || token.typ == yaml_DOCUMENT_START_TOKEN || @@ -299,9 +383,10 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* -// ************* -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // +// ************* +// +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -327,35 +412,62 @@ func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) end_mark: end_mark, implicit: implicit, } + yaml_parser_set_event_comments(parser, event) + if len(event.head_comment) > 0 && len(event.foot_comment) == 0 { + event.foot_comment = event.head_comment + event.head_comment = nil + } return true } +func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) { + event.head_comment = parser.head_comment + event.line_comment = parser.line_comment + event.foot_comment = parser.foot_comment + parser.head_comment = nil + parser.line_comment = nil + parser.foot_comment = nil + parser.tail_comment = nil + parser.stem_comment = nil +} + // Parse the productions: // block_node_or_indentless_sequence ::= -// ALIAS -// ***** -// | properties (block_content | indentless_block_sequence)? -// ********** * -// | block_content | indentless_block_sequence -// * +// +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * +// // block_node ::= ALIAS -// ***** -// | properties block_content? -// ********** * -// | block_content -// * +// +// ***** +// | properties block_content? +// ********** * +// | block_content +// * +// // flow_node ::= ALIAS -// ***** -// | properties flow_content? -// ********** * -// | flow_content -// * +// +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * +// // properties ::= TAG ANCHOR? | ANCHOR TAG? -// ************************* +// +// ************************* +// // block_content ::= block_collection | flow_collection | SCALAR -// ****** +// +// ****** +// // flow_content ::= flow_collection | SCALAR -// ****** +// +// ****** func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() @@ -373,6 +485,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i end_mark: token.end_mark, anchor: token.value, } + yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } @@ -486,6 +599,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i quoted_implicit: quoted_implicit, style: yaml_style_t(token.style), } + yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } @@ -502,6 +616,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i implicit: implicit, style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), } + yaml_parser_set_event_comments(parser, event) return true } if token.typ == yaml_FLOW_MAPPING_START_TOKEN { @@ -516,6 +631,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i implicit: implicit, style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), } + yaml_parser_set_event_comments(parser, event) return true } if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { @@ -530,6 +646,10 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i implicit: implicit, style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), } + if parser.stem_comment != nil { + event.head_comment = parser.stem_comment + parser.stem_comment = nil + } return true } if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { @@ -544,6 +664,10 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i implicit: implicit, style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), } + if parser.stem_comment != nil { + event.head_comment = parser.stem_comment + parser.stem_comment = nil + } return true } if len(anchor) > 0 || len(tag) > 0 { @@ -574,11 +698,14 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i // Parse the productions: // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END -// ******************** *********** * ********* // +// ******************** *********** * ********* func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -590,7 +717,9 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e if token.typ == yaml_BLOCK_ENTRY_TOKEN { mark := token.end_mark + prior_head_len := len(parser.head_comment) skip_token(parser) + yaml_parser_split_stem_comment(parser, prior_head_len) token = peek_token(parser) if token == nil { return false @@ -627,7 +756,8 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e // Parse the productions: // indentless_sequence ::= (BLOCK-ENTRY block_node?)+ -// *********** * +// +// *********** * func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -636,7 +766,9 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y if token.typ == yaml_BLOCK_ENTRY_TOKEN { mark := token.end_mark + prior_head_len := len(parser.head_comment) skip_token(parser) + yaml_parser_split_stem_comment(parser, prior_head_len) token = peek_token(parser) if token == nil { return false @@ -662,19 +794,48 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y return true } +// Split stem comment from head comment. +// +// When a sequence or map is found under a sequence entry, the former head comment +// is assigned to the underlying sequence or map as a whole, not the individual +// sequence or map entry as would be expected otherwise. To handle this case the +// previous head comment is moved aside as the stem comment. +func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { + if stem_len == 0 { + return + } + + token := peek_token(parser) + if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { + return + } + + parser.stem_comment = parser.head_comment[:stem_len] + if len(parser.head_comment) == stem_len { + parser.head_comment = nil + } else { + // Copy suffix to prevent very strange bugs if someone ever appends + // further bytes to the prefix in the stem_comment slice above. + parser.head_comment = append([]byte(nil), parser.head_comment[stem_len+1:]...) + } +} + // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START -// ******************* -// ((KEY block_node_or_indentless_sequence?)? -// *** * -// (VALUE block_node_or_indentless_sequence?)?)* // -// BLOCK-END -// ********* +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* // +// BLOCK-END +// ********* func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -684,6 +845,19 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even return false } + // [Go] A tail comment was left from the prior mapping value processed. Emit an event + // as it needs to be processed with that value and not the following key. + if len(parser.tail_comment) > 0 { + *event = yaml_event_t{ + typ: yaml_TAIL_COMMENT_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + foot_comment: parser.tail_comment, + } + parser.tail_comment = nil + return true + } + if token.typ == yaml_KEY_TOKEN { mark := token.end_mark skip_token(parser) @@ -709,6 +883,7 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even start_mark: token.start_mark, end_mark: token.end_mark, } + yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } @@ -723,13 +898,11 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START // -// ((KEY block_node_or_indentless_sequence?)? -// -// (VALUE block_node_or_indentless_sequence?)?)* -// ***** * -// BLOCK-END -// +// ((KEY block_node_or_indentless_sequence?)? // +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -757,19 +930,24 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev // Parse the productions: // flow_sequence ::= FLOW-SEQUENCE-START -// ******************* -// (flow_sequence_entry FLOW-ENTRY)* -// * ********** -// flow_sequence_entry? -// * -// FLOW-SEQUENCE-END -// ***************** +// +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** +// // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// * // +// * func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -820,16 +998,16 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev start_mark: token.start_mark, end_mark: token.end_mark, } + yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } -// // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// *** * // +// *** * func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -849,8 +1027,8 @@ func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, ev // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// ***** * // +// ***** * func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -873,8 +1051,8 @@ func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// * // +// * func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -891,16 +1069,17 @@ func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, ev // Parse the productions: // flow_mapping ::= FLOW-MAPPING-START -// ****************** -// (flow_mapping_entry FLOW-ENTRY)* -// * ********** -// flow_mapping_entry? -// ****************** -// FLOW-MAPPING-END -// **************** -// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// * *** * // +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** +// +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// - *** * func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -959,14 +1138,14 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event start_mark: token.start_mark, end_mark: token.end_mark, } + yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } // Parse the productions: // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// * ***** * -// +// - ***** * func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { token := peek_token(parser) if token == nil { diff --git a/vendor/gopkg.in/yaml.v2/readerc.go b/vendor/go.yaml.in/yaml/v3/readerc.go similarity index 91% rename from vendor/gopkg.in/yaml.v2/readerc.go rename to vendor/go.yaml.in/yaml/v3/readerc.go index 7c1f5fac3..56af24536 100644 --- a/vendor/gopkg.in/yaml.v2/readerc.go +++ b/vendor/go.yaml.in/yaml/v3/readerc.go @@ -1,3 +1,25 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + package yaml import ( @@ -95,7 +117,7 @@ func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { // [Go] This function was changed to guarantee the requested length size at EOF. // The fact we need to do this is pretty awful, but the description above implies - // for that to be the case, and there are tests + // for that to be the case, and there are tests // If the EOF flag is set and the raw buffer is empty, do nothing. if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/go.yaml.in/yaml/v3/resolve.go similarity index 60% rename from vendor/gopkg.in/yaml.v2/resolve.go rename to vendor/go.yaml.in/yaml/v3/resolve.go index 4120e0c91..64ae88805 100644 --- a/vendor/gopkg.in/yaml.v2/resolve.go +++ b/vendor/go.yaml.in/yaml/v3/resolve.go @@ -1,3 +1,18 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package yaml import ( @@ -34,18 +49,14 @@ func init() { tag string l []string }{ - {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, - {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, - {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, - {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, - {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, - {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, - {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, - {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, - {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, - {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, - {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, - {"<<", yaml_MERGE_TAG, []string{"<<"}}, + {true, boolTag, []string{"true", "True", "TRUE"}}, + {false, boolTag, []string{"false", "False", "FALSE"}}, + {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, + {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, + {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, + {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, + {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, + {"<<", mergeTag, []string{"<<"}}, } m := resolveMap @@ -56,11 +67,37 @@ func init() { } } +const ( + nullTag = "!!null" + boolTag = "!!bool" + strTag = "!!str" + intTag = "!!int" + floatTag = "!!float" + timestampTag = "!!timestamp" + seqTag = "!!seq" + mapTag = "!!map" + binaryTag = "!!binary" + mergeTag = "!!merge" +) + +var longTags = make(map[string]string) +var shortTags = make(map[string]string) + +func init() { + for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { + ltag := longTag(stag) + longTags[stag] = ltag + shortTags[ltag] = stag + } +} + const longTagPrefix = "tag:yaml.org,2002:" func shortTag(tag string) string { - // TODO This can easily be made faster and produce less garbage. if strings.HasPrefix(tag, longTagPrefix) { + if stag, ok := shortTags[tag]; ok { + return stag + } return "!!" + tag[len(longTagPrefix):] } return tag @@ -68,6 +105,9 @@ func shortTag(tag string) string { func longTag(tag string) string { if strings.HasPrefix(tag, "!!") { + if ltag, ok := longTags[tag]; ok { + return ltag + } return longTagPrefix + tag[2:] } return tag @@ -75,7 +115,7 @@ func longTag(tag string) string { func resolvableTag(tag string) bool { switch tag { - case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG: + case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: return true } return false @@ -84,23 +124,24 @@ func resolvableTag(tag string) bool { var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) func resolve(tag string, in string) (rtag string, out interface{}) { + tag = shortTag(tag) if !resolvableTag(tag) { return tag, in } defer func() { switch tag { - case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: + case "", rtag, strTag, binaryTag: return - case yaml_FLOAT_TAG: - if rtag == yaml_INT_TAG { + case floatTag: + if rtag == intTag { switch v := out.(type) { case int64: - rtag = yaml_FLOAT_TAG + rtag = floatTag out = float64(v) return case int: - rtag = yaml_FLOAT_TAG + rtag = floatTag out = float64(v) return } @@ -115,7 +156,7 @@ func resolve(tag string, in string) (rtag string, out interface{}) { if in != "" { hint = resolveTable[in[0]] } - if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { + if hint != 0 && tag != strTag && tag != binaryTag { // Handle things we can lookup in a map. if item, ok := resolveMap[in]; ok { return item.tag, item.value @@ -133,17 +174,17 @@ func resolve(tag string, in string) (rtag string, out interface{}) { // Not in the map, so maybe a normal float. floatv, err := strconv.ParseFloat(in, 64) if err == nil { - return yaml_FLOAT_TAG, floatv + return floatTag, floatv } case 'D', 'S': // Int, float, or timestamp. // Only try values as a timestamp if the value is unquoted or there's an explicit // !!timestamp tag. - if tag == "" || tag == yaml_TIMESTAMP_TAG { + if tag == "" || tag == timestampTag { t, ok := parseTimestamp(in) if ok { - return yaml_TIMESTAMP_TAG, t + return timestampTag, t } } @@ -151,49 +192,76 @@ func resolve(tag string, in string) (rtag string, out interface{}) { intv, err := strconv.ParseInt(plain, 0, 64) if err == nil { if intv == int64(int(intv)) { - return yaml_INT_TAG, int(intv) + return intTag, int(intv) } else { - return yaml_INT_TAG, intv + return intTag, intv } } uintv, err := strconv.ParseUint(plain, 0, 64) if err == nil { - return yaml_INT_TAG, uintv + return intTag, uintv } if yamlStyleFloat.MatchString(plain) { floatv, err := strconv.ParseFloat(plain, 64) if err == nil { - return yaml_FLOAT_TAG, floatv + return floatTag, floatv } } if strings.HasPrefix(plain, "0b") { intv, err := strconv.ParseInt(plain[2:], 2, 64) if err == nil { if intv == int64(int(intv)) { - return yaml_INT_TAG, int(intv) + return intTag, int(intv) } else { - return yaml_INT_TAG, intv + return intTag, intv } } uintv, err := strconv.ParseUint(plain[2:], 2, 64) if err == nil { - return yaml_INT_TAG, uintv + return intTag, uintv } } else if strings.HasPrefix(plain, "-0b") { - intv, err := strconv.ParseInt("-" + plain[3:], 2, 64) + intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) + if err == nil { + if true || intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + } + // Octals as introduced in version 1.2 of the spec. + // Octals from the 1.1 spec, spelled as 0777, are still + // decoded by default in v3 as well for compatibility. + // May be dropped in v4 depending on how usage evolves. + if strings.HasPrefix(plain, "0o") { + intv, err := strconv.ParseInt(plain[2:], 8, 64) + if err == nil { + if intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + uintv, err := strconv.ParseUint(plain[2:], 8, 64) + if err == nil { + return intTag, uintv + } + } else if strings.HasPrefix(plain, "-0o") { + intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) if err == nil { if true || intv == int64(int(intv)) { - return yaml_INT_TAG, int(intv) + return intTag, int(intv) } else { - return yaml_INT_TAG, intv + return intTag, intv } } } default: - panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") + panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") } } - return yaml_STR_TAG, in + return strTag, in } // encodeBase64 encodes s as base64 that is broken up into multiple lines diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/go.yaml.in/yaml/v3/scannerc.go similarity index 86% rename from vendor/gopkg.in/yaml.v2/scannerc.go rename to vendor/go.yaml.in/yaml/v3/scannerc.go index 0b9bb6030..30b1f0892 100644 --- a/vendor/gopkg.in/yaml.v2/scannerc.go +++ b/vendor/go.yaml.in/yaml/v3/scannerc.go @@ -1,3 +1,25 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + package yaml import ( @@ -489,6 +511,9 @@ func cache(parser *yaml_parser_t, length int) bool { // Advance the buffer pointer. func skip(parser *yaml_parser_t) { + if !is_blank(parser.buffer, parser.buffer_pos) { + parser.newlines = 0 + } parser.mark.index++ parser.mark.column++ parser.unread-- @@ -502,17 +527,22 @@ func skip_line(parser *yaml_parser_t) { parser.mark.line++ parser.unread -= 2 parser.buffer_pos += 2 + parser.newlines++ } else if is_break(parser.buffer, parser.buffer_pos) { parser.mark.index++ parser.mark.column = 0 parser.mark.line++ parser.unread-- parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) + parser.newlines++ } } // Copy a character to a string buffer and advance pointers. func read(parser *yaml_parser_t, s []byte) []byte { + if !is_blank(parser.buffer, parser.buffer_pos) { + parser.newlines = 0 + } w := width(parser.buffer[parser.buffer_pos]) if w == 0 { panic("invalid character sequence") @@ -564,6 +594,7 @@ func read_line(parser *yaml_parser_t, s []byte) []byte { parser.mark.column = 0 parser.mark.line++ parser.unread-- + parser.newlines++ return s } @@ -626,9 +657,13 @@ func trace(args ...interface{}) func() { func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { // While we need more tokens to fetch, do it. for { - if parser.tokens_head != len(parser.tokens) { - // If queue is non-empty, check if any potential simple key may - // occupy the head position. + // [Go] The comment parsing logic requires a lookahead of two tokens + // so that foot comments may be parsed in time of associating them + // with the tokens that are parsed before them, and also for line + // comments to be transformed into head comments in some edge cases. + if parser.tokens_head < len(parser.tokens)-2 { + // If a potential simple key is at the head position, we need to fetch + // the next token to disambiguate it. head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed] if !ok { break @@ -649,7 +684,7 @@ func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { } // The dispatcher for token fetchers. -func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { +func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) { // Ensure that the buffer is initialized. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false @@ -660,13 +695,19 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { return yaml_parser_fetch_stream_start(parser) } + scan_mark := parser.mark + // Eat whitespaces and comments until we reach the next token. if !yaml_parser_scan_to_next_token(parser) { return false } + // [Go] While unrolling indents, transform the head comments of prior + // indentation levels observed after scan_start into foot comments at + // the respective indexes. + // Check the indentation level against the current column. - if !yaml_parser_unroll_indent(parser, parser.mark.column) { + if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) { return false } @@ -699,6 +740,26 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) } + comment_mark := parser.mark + if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') { + // Associate any following comments with the prior token. + comment_mark = parser.tokens[len(parser.tokens)-1].start_mark + } + defer func() { + if !ok { + return + } + if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN { + // Sequence indicators alone have no line comments. It becomes + // a head comment for whatever follows. + return + } + if !yaml_parser_scan_line_comment(parser, comment_mark) { + ok = false + return + } + }() + // Is it the flow sequence start indicator? if buf[pos] == '[' { return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) @@ -792,7 +853,7 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { // if it is followed by a non-space character. // // The last rule is more restrictive than the specification requires. - // [Go] Make this logic more reasonable. + // [Go] TODO Make this logic more reasonable. //switch parser.buffer[parser.buffer_pos] { //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': //} @@ -965,19 +1026,49 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml // Pop indentation levels from the indents stack until the current level // becomes less or equal to the column. For each indentation level, append // the BLOCK-END token. -func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { +func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool { // In the flow context, do nothing. if parser.flow_level > 0 { return true } + block_mark := scan_mark + block_mark.index-- + // Loop through the indentation levels in the stack. for parser.indent > column { + + // [Go] Reposition the end token before potential following + // foot comments of parent blocks. For that, search + // backwards for recent comments that were at the same + // indent as the block that is ending now. + stop_index := block_mark.index + for i := len(parser.comments) - 1; i >= 0; i-- { + comment := &parser.comments[i] + + if comment.end_mark.index < stop_index { + // Don't go back beyond the start of the comment/whitespace scan, unless column < 0. + // If requested indent column is < 0, then the document is over and everything else + // is a foot anyway. + break + } + if comment.start_mark.column == parser.indent+1 { + // This is a good match. But maybe there's a former comment + // at that same indent level, so keep searching. + block_mark = comment.start_mark + } + + // While the end of the former comment matches with + // the start of the following one, we know there's + // nothing in between and scanning is still safe. + stop_index = comment.scan_mark.index + } + // Create a token and append it to the queue. token := yaml_token_t{ typ: yaml_BLOCK_END_TOKEN, - start_mark: parser.mark, - end_mark: parser.mark, + start_mark: block_mark, + end_mark: block_mark, } yaml_insert_token(parser, -1, &token) @@ -1026,7 +1117,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { } // Reset the indentation level. - if !yaml_parser_unroll_indent(parser, -1) { + if !yaml_parser_unroll_indent(parser, -1, parser.mark) { return false } @@ -1050,7 +1141,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { // Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { // Reset the indentation level. - if !yaml_parser_unroll_indent(parser, -1) { + if !yaml_parser_unroll_indent(parser, -1, parser.mark) { return false } @@ -1074,7 +1165,7 @@ func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { // Produce the DOCUMENT-START or DOCUMENT-END token. func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { // Reset the indentation level. - if !yaml_parser_unroll_indent(parser, -1) { + if !yaml_parser_unroll_indent(parser, -1, parser.mark) { return false } @@ -1107,6 +1198,7 @@ func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_ // Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // The indicators '[' and '{' may start a simple key. if !yaml_parser_save_simple_key(parser) { return false @@ -1442,6 +1534,8 @@ func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { // Eat whitespaces and comments until the next token is found. func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { + scan_mark := parser.mark + // Until the next token is not found. for { // Allow the BOM mark to start a line. @@ -1468,13 +1562,33 @@ func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { } } + // Check if we just had a line comment under a sequence entry that + // looks more like a header to the following content. Similar to this: + // + // - # The comment + // - Some data + // + // If so, transform the line comment to a head comment and reposition. + if len(parser.comments) > 0 && len(parser.tokens) > 1 { + tokenA := parser.tokens[len(parser.tokens)-2] + tokenB := parser.tokens[len(parser.tokens)-1] + comment := &parser.comments[len(parser.comments)-1] + if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) { + // If it was in the prior line, reposition so it becomes a + // header of the follow up token. Otherwise, keep it in place + // so it becomes a header of the former. + comment.head = comment.line + comment.line = nil + if comment.start_mark.line == parser.mark.line-1 { + comment.token_mark = parser.mark + } + } + } + // Eat a comment until a line break. if parser.buffer[parser.buffer_pos] == '#' { - for !is_breakz(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } + if !yaml_parser_scan_comments(parser, scan_mark) { + return false } } @@ -1500,11 +1614,11 @@ func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { // Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. // // Scope: -// %YAML 1.1 # a comment \n -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -// %TAG !yaml! tag:yaml.org,2002: \n -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// %YAML 1.1 # a comment \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { // Eat '%'. start_mark := parser.mark @@ -1572,6 +1686,10 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool } if parser.buffer[parser.buffer_pos] == '#' { + // [Go] Discard this inline comment for the time being. + //if !yaml_parser_scan_line_comment(parser, start_mark) { + // return false + //} for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { @@ -1601,11 +1719,11 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool // Scan the directive name. // // Scope: -// %YAML 1.1 # a comment \n -// ^^^^ -// %TAG !yaml! tag:yaml.org,2002: \n -// ^^^ // +// %YAML 1.1 # a comment \n +// ^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^ func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { // Consume the directive name. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { @@ -1640,8 +1758,9 @@ func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark // Scan the value of VERSION-DIRECTIVE. // // Scope: -// %YAML 1.1 # a comment \n -// ^^^^^^ +// +// %YAML 1.1 # a comment \n +// ^^^^^^ func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { // Eat whitespaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { @@ -1679,10 +1798,11 @@ const max_number_length = 2 // Scan the version number of VERSION-DIRECTIVE. // // Scope: -// %YAML 1.1 # a comment \n -// ^ -// %YAML 1.1 # a comment \n -// ^ +// +// %YAML 1.1 # a comment \n +// ^ +// %YAML 1.1 # a comment \n +// ^ func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { // Repeat while the next character is digit. @@ -1716,9 +1836,9 @@ func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark // Scan the value of a TAG-DIRECTIVE token. // // Scope: -// %TAG !yaml! tag:yaml.org,2002: \n -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { var handle_value, prefix_value []byte @@ -1987,7 +2107,7 @@ func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', // '%'. - // [Go] Convert this into more reasonable logic. + // [Go] TODO Convert this into more reasonable logic. for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || @@ -2142,6 +2262,9 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l } } if parser.buffer[parser.buffer_pos] == '#' { + if !yaml_parser_scan_line_comment(parser, start_mark) { + return false + } for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { @@ -2709,3 +2832,209 @@ func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) b } return true } + +func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool { + if parser.newlines > 0 { + return true + } + + var start_mark yaml_mark_t + var text []byte + + for peek := 0; peek < 512; peek++ { + if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { + break + } + if is_blank(parser.buffer, parser.buffer_pos+peek) { + continue + } + if parser.buffer[parser.buffer_pos+peek] == '#' { + seen := parser.mark.index + peek + for { + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_breakz(parser.buffer, parser.buffer_pos) { + if parser.mark.index >= seen { + break + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } else if parser.mark.index >= seen { + if len(text) == 0 { + start_mark = parser.mark + } + text = read(parser, text) + } else { + skip(parser) + } + } + } + break + } + if len(text) > 0 { + parser.comments = append(parser.comments, yaml_comment_t{ + token_mark: token_mark, + start_mark: start_mark, + line: text, + }) + } + return true +} + +func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool { + token := parser.tokens[len(parser.tokens)-1] + + if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 { + token = parser.tokens[len(parser.tokens)-2] + } + + var token_mark = token.start_mark + var start_mark yaml_mark_t + var next_indent = parser.indent + if next_indent < 0 { + next_indent = 0 + } + + var recent_empty = false + var first_empty = parser.newlines <= 1 + + var line = parser.mark.line + var column = parser.mark.column + + var text []byte + + // The foot line is the place where a comment must start to + // still be considered as a foot of the prior content. + // If there's some content in the currently parsed line, then + // the foot is the line below it. + var foot_line = -1 + if scan_mark.line > 0 { + foot_line = parser.mark.line - parser.newlines + 1 + if parser.newlines == 0 && parser.mark.column > 1 { + foot_line++ + } + } + + var peek = 0 + for ; peek < 512; peek++ { + if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { + break + } + column++ + if is_blank(parser.buffer, parser.buffer_pos+peek) { + continue + } + c := parser.buffer[parser.buffer_pos+peek] + var close_flow = parser.flow_level > 0 && (c == ']' || c == '}') + if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) { + // Got line break or terminator. + if close_flow || !recent_empty { + if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) { + // This is the first empty line and there were no empty lines before, + // so this initial part of the comment is a foot of the prior token + // instead of being a head for the following one. Split it up. + // Alternatively, this might also be the last comment inside a flow + // scope, so it must be a footer. + if len(text) > 0 { + if start_mark.column-1 < next_indent { + // If dedented it's unrelated to the prior token. + token_mark = start_mark + } + parser.comments = append(parser.comments, yaml_comment_t{ + scan_mark: scan_mark, + token_mark: token_mark, + start_mark: start_mark, + end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, + foot: text, + }) + scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} + token_mark = scan_mark + text = nil + } + } else { + if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 { + text = append(text, '\n') + } + } + } + if !is_break(parser.buffer, parser.buffer_pos+peek) { + break + } + first_empty = false + recent_empty = true + column = 0 + line++ + continue + } + + if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) { + // The comment at the different indentation is a foot of the + // preceding data rather than a head of the upcoming one. + parser.comments = append(parser.comments, yaml_comment_t{ + scan_mark: scan_mark, + token_mark: token_mark, + start_mark: start_mark, + end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, + foot: text, + }) + scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} + token_mark = scan_mark + text = nil + } + + if parser.buffer[parser.buffer_pos+peek] != '#' { + break + } + + if len(text) == 0 { + start_mark = yaml_mark_t{parser.mark.index + peek, line, column} + } else { + text = append(text, '\n') + } + + recent_empty = false + + // Consume until after the consumed comment line. + seen := parser.mark.index + peek + for { + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_breakz(parser.buffer, parser.buffer_pos) { + if parser.mark.index >= seen { + break + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } else if parser.mark.index >= seen { + text = read(parser, text) + } else { + skip(parser) + } + } + + peek = 0 + column = 0 + line = parser.mark.line + next_indent = parser.indent + if next_indent < 0 { + next_indent = 0 + } + } + + if len(text) > 0 { + parser.comments = append(parser.comments, yaml_comment_t{ + scan_mark: scan_mark, + token_mark: start_mark, + start_mark: start_mark, + end_mark: yaml_mark_t{parser.mark.index + peek - 1, line, column}, + head: text, + }) + } + return true +} diff --git a/vendor/gopkg.in/yaml.v2/sorter.go b/vendor/go.yaml.in/yaml/v3/sorter.go similarity index 76% rename from vendor/gopkg.in/yaml.v2/sorter.go rename to vendor/go.yaml.in/yaml/v3/sorter.go index 4c45e660a..9210ece7e 100644 --- a/vendor/gopkg.in/yaml.v2/sorter.go +++ b/vendor/go.yaml.in/yaml/v3/sorter.go @@ -1,3 +1,18 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package yaml import ( @@ -37,8 +52,10 @@ func (l keyList) Less(i, j int) bool { return ak < bk } ar, br := []rune(a.String()), []rune(b.String()) + digits := false for i := 0; i < len(ar) && i < len(br); i++ { if ar[i] == br[i] { + digits = unicode.IsDigit(ar[i]) continue } al := unicode.IsLetter(ar[i]) @@ -47,12 +64,16 @@ func (l keyList) Less(i, j int) bool { return ar[i] < br[i] } if al || bl { - return bl + if digits { + return al + } else { + return bl + } } var ai, bi int var an, bn int64 if ar[i] == '0' || br[i] == '0' { - for j := i-1; j >= 0 && unicode.IsDigit(ar[j]); j-- { + for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { if ar[j] != '0' { an = 1 bn = 1 diff --git a/vendor/go.yaml.in/yaml/v3/writerc.go b/vendor/go.yaml.in/yaml/v3/writerc.go new file mode 100644 index 000000000..266d0b092 --- /dev/null +++ b/vendor/go.yaml.in/yaml/v3/writerc.go @@ -0,0 +1,48 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +// Set the writer error and return false. +func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_WRITER_ERROR + emitter.problem = problem + return false +} + +// Flush the output buffer. +func yaml_emitter_flush(emitter *yaml_emitter_t) bool { + if emitter.write_handler == nil { + panic("write handler not set") + } + + // Check if the buffer is empty. + if emitter.buffer_pos == 0 { + return true + } + + if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { + return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) + } + emitter.buffer_pos = 0 + return true +} diff --git a/vendor/go.yaml.in/yaml/v3/yaml.go b/vendor/go.yaml.in/yaml/v3/yaml.go new file mode 100644 index 000000000..0b101cd20 --- /dev/null +++ b/vendor/go.yaml.in/yaml/v3/yaml.go @@ -0,0 +1,703 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package yaml implements YAML support for the Go language. +// +// Source code and other details for the project are available at GitHub: +// +// https://github.com/yaml/go-yaml +package yaml + +import ( + "errors" + "fmt" + "io" + "reflect" + "strings" + "sync" + "unicode/utf8" +) + +// The Unmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a YAML document. +type Unmarshaler interface { + UnmarshalYAML(value *Node) error +} + +type obsoleteUnmarshaler interface { + UnmarshalYAML(unmarshal func(interface{}) error) error +} + +// The Marshaler interface may be implemented by types to customize their +// behavior when being marshaled into a YAML document. The returned value +// is marshaled in place of the original value implementing Marshaler. +// +// If an error is returned by MarshalYAML, the marshaling procedure stops +// and returns with the provided error. +type Marshaler interface { + MarshalYAML() (interface{}, error) +} + +// Unmarshal decodes the first document found within the in byte slice +// and assigns decoded values into the out value. +// +// Maps and pointers (to a struct, string, int, etc) are accepted as out +// values. If an internal pointer within a struct is not initialized, +// the yaml package will initialize it if necessary for unmarshalling +// the provided data. The out parameter must not be nil. +// +// The type of the decoded values should be compatible with the respective +// values in out. If one or more values cannot be decoded due to a type +// mismatches, decoding continues partially until the end of the YAML +// content, and a *yaml.TypeError is returned with details for all +// missed values. +// +// Struct fields are only unmarshalled if they are exported (have an +// upper case first letter), and are unmarshalled using the field name +// lowercased as the default key. Custom keys may be defined via the +// "yaml" name in the field tag: the content preceding the first comma +// is used as the key, and the following comma-separated options are +// used to tweak the marshalling process (see Marshal). +// Conflicting names result in a runtime error. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// +// See the documentation of Marshal for the format of tags and a list of +// supported tag options. +func Unmarshal(in []byte, out interface{}) (err error) { + return unmarshal(in, out, false) +} + +// A Decoder reads and decodes YAML values from an input stream. +type Decoder struct { + parser *parser + knownFields bool +} + +// NewDecoder returns a new decoder that reads from r. +// +// The decoder introduces its own buffering and may read +// data from r beyond the YAML values requested. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{ + parser: newParserFromReader(r), + } +} + +// KnownFields ensures that the keys in decoded mappings to +// exist as fields in the struct being decoded into. +func (dec *Decoder) KnownFields(enable bool) { + dec.knownFields = enable +} + +// Decode reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (dec *Decoder) Decode(v interface{}) (err error) { + d := newDecoder() + d.knownFields = dec.knownFields + defer handleErr(&err) + node := dec.parser.parse() + if node == nil { + return io.EOF + } + out := reflect.ValueOf(v) + if out.Kind() == reflect.Ptr && !out.IsNil() { + out = out.Elem() + } + d.unmarshal(node, out) + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Decode decodes the node and stores its data into the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (n *Node) Decode(v interface{}) (err error) { + d := newDecoder() + defer handleErr(&err) + out := reflect.ValueOf(v) + if out.Kind() == reflect.Ptr && !out.IsNil() { + out = out.Elem() + } + d.unmarshal(n, out) + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +func unmarshal(in []byte, out interface{}, strict bool) (err error) { + defer handleErr(&err) + d := newDecoder() + p := newParser(in) + defer p.destroy() + node := p.parse() + if node != nil { + v := reflect.ValueOf(out) + if v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + d.unmarshal(node, v) + } + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Marshal serializes the value provided into a YAML document. The structure +// of the generated document will reflect the structure of the value itself. +// Maps and pointers (to struct, string, int, etc) are accepted as the in value. +// +// Struct fields are only marshalled if they are exported (have an upper case +// first letter), and are marshalled using the field name lowercased as the +// default key. Custom keys may be defined via the "yaml" name in the field +// tag: the content preceding the first comma is used as the key, and the +// following comma-separated options are used to tweak the marshalling process. +// Conflicting names result in a runtime error. +// +// The field tag format accepted is: +// +// `(...) yaml:"[][,[,]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Zero valued structs will be omitted if all their public +// fields are zero, unless they implement an IsZero +// method (see the IsZeroer interface type), in which +// case the field will be excluded if IsZero returns true. +// +// flow Marshal using a flow style (useful for structs, +// sequences and maps). +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. +// +// In addition, if the key is "-", the field is ignored. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" +func Marshal(in interface{}) (out []byte, err error) { + defer handleErr(&err) + e := newEncoder() + defer e.destroy() + e.marshalDoc("", reflect.ValueOf(in)) + e.finish() + out = e.out + return +} + +// An Encoder writes YAML values to an output stream. +type Encoder struct { + encoder *encoder +} + +// NewEncoder returns a new encoder that writes to w. +// The Encoder should be closed after use to flush all data +// to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + encoder: newEncoderWithWriter(w), + } +} + +// Encode writes the YAML encoding of v to the stream. +// If multiple items are encoded to the stream, the +// second and subsequent document will be preceded +// with a "---" document separator, but the first will not. +// +// See the documentation for Marshal for details about the conversion of Go +// values to YAML. +func (e *Encoder) Encode(v interface{}) (err error) { + defer handleErr(&err) + e.encoder.marshalDoc("", reflect.ValueOf(v)) + return nil +} + +// Encode encodes value v and stores its representation in n. +// +// See the documentation for Marshal for details about the +// conversion of Go values into YAML. +func (n *Node) Encode(v interface{}) (err error) { + defer handleErr(&err) + e := newEncoder() + defer e.destroy() + e.marshalDoc("", reflect.ValueOf(v)) + e.finish() + p := newParser(e.out) + p.textless = true + defer p.destroy() + doc := p.parse() + *n = *doc.Content[0] + return nil +} + +// SetIndent changes the used indentation used when encoding. +func (e *Encoder) SetIndent(spaces int) { + if spaces < 0 { + panic("yaml: cannot indent to a negative number of spaces") + } + e.encoder.indent = spaces +} + +// CompactSeqIndent makes it so that '- ' is considered part of the indentation. +func (e *Encoder) CompactSeqIndent() { + e.encoder.emitter.compact_sequence_indent = true +} + +// DefaultSeqIndent makes it so that '- ' is not considered part of the indentation. +func (e *Encoder) DefaultSeqIndent() { + e.encoder.emitter.compact_sequence_indent = false +} + +// Close closes the encoder by writing any remaining data. +// It does not write a stream terminating string "...". +func (e *Encoder) Close() (err error) { + defer handleErr(&err) + e.encoder.finish() + return nil +} + +func handleErr(err *error) { + if v := recover(); v != nil { + if e, ok := v.(yamlError); ok { + *err = e.err + } else { + panic(v) + } + } +} + +type yamlError struct { + err error +} + +func fail(err error) { + panic(yamlError{err}) +} + +func failf(format string, args ...interface{}) { + panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) +} + +// A TypeError is returned by Unmarshal when one or more fields in +// the YAML document cannot be properly decoded into the requested +// types. When this error is returned, the value is still +// unmarshaled partially. +type TypeError struct { + Errors []string +} + +func (e *TypeError) Error() string { + return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) +} + +type Kind uint32 + +const ( + DocumentNode Kind = 1 << iota + SequenceNode + MappingNode + ScalarNode + AliasNode +) + +type Style uint32 + +const ( + TaggedStyle Style = 1 << iota + DoubleQuotedStyle + SingleQuotedStyle + LiteralStyle + FoldedStyle + FlowStyle +) + +// Node represents an element in the YAML document hierarchy. While documents +// are typically encoded and decoded into higher level types, such as structs +// and maps, Node is an intermediate representation that allows detailed +// control over the content being decoded or encoded. +// +// It's worth noting that although Node offers access into details such as +// line numbers, colums, and comments, the content when re-encoded will not +// have its original textual representation preserved. An effort is made to +// render the data plesantly, and to preserve comments near the data they +// describe, though. +// +// Values that make use of the Node type interact with the yaml package in the +// same way any other type would do, by encoding and decoding yaml data +// directly or indirectly into them. +// +// For example: +// +// var person struct { +// Name string +// Address yaml.Node +// } +// err := yaml.Unmarshal(data, &person) +// +// Or by itself: +// +// var person Node +// err := yaml.Unmarshal(data, &person) +type Node struct { + // Kind defines whether the node is a document, a mapping, a sequence, + // a scalar value, or an alias to another node. The specific data type of + // scalar nodes may be obtained via the ShortTag and LongTag methods. + Kind Kind + + // Style allows customizing the apperance of the node in the tree. + Style Style + + // Tag holds the YAML tag defining the data type for the value. + // When decoding, this field will always be set to the resolved tag, + // even when it wasn't explicitly provided in the YAML content. + // When encoding, if this field is unset the value type will be + // implied from the node properties, and if it is set, it will only + // be serialized into the representation if TaggedStyle is used or + // the implicit tag diverges from the provided one. + Tag string + + // Value holds the unescaped and unquoted represenation of the value. + Value string + + // Anchor holds the anchor name for this node, which allows aliases to point to it. + Anchor string + + // Alias holds the node that this alias points to. Only valid when Kind is AliasNode. + Alias *Node + + // Content holds contained nodes for documents, mappings, and sequences. + Content []*Node + + // HeadComment holds any comments in the lines preceding the node and + // not separated by an empty line. + HeadComment string + + // LineComment holds any comments at the end of the line where the node is in. + LineComment string + + // FootComment holds any comments following the node and before empty lines. + FootComment string + + // Line and Column hold the node position in the decoded YAML text. + // These fields are not respected when encoding the node. + Line int + Column int +} + +// IsZero returns whether the node has all of its fields unset. +func (n *Node) IsZero() bool { + return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil && + n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 +} + +// LongTag returns the long form of the tag that indicates the data type for +// the node. If the Tag field isn't explicitly defined, one will be computed +// based on the node properties. +func (n *Node) LongTag() string { + return longTag(n.ShortTag()) +} + +// ShortTag returns the short form of the YAML tag that indicates data type for +// the node. If the Tag field isn't explicitly defined, one will be computed +// based on the node properties. +func (n *Node) ShortTag() string { + if n.indicatedString() { + return strTag + } + if n.Tag == "" || n.Tag == "!" { + switch n.Kind { + case MappingNode: + return mapTag + case SequenceNode: + return seqTag + case AliasNode: + if n.Alias != nil { + return n.Alias.ShortTag() + } + case ScalarNode: + tag, _ := resolve("", n.Value) + return tag + case 0: + // Special case to make the zero value convenient. + if n.IsZero() { + return nullTag + } + } + return "" + } + return shortTag(n.Tag) +} + +func (n *Node) indicatedString() bool { + return n.Kind == ScalarNode && + (shortTag(n.Tag) == strTag || + (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) +} + +// SetString is a convenience function that sets the node to a string value +// and defines its style in a pleasant way depending on its content. +func (n *Node) SetString(s string) { + n.Kind = ScalarNode + if utf8.ValidString(s) { + n.Value = s + n.Tag = strTag + } else { + n.Value = encodeBase64(s) + n.Tag = binaryTag + } + if strings.Contains(n.Value, "\n") { + n.Style = LiteralStyle + } +} + +// -------------------------------------------------------------------------- +// Maintain a mapping of keys to structure field indexes + +// The code in this section was copied from mgo/bson. + +// structInfo holds details for the serialization of fields of +// a given struct. +type structInfo struct { + FieldsMap map[string]fieldInfo + FieldsList []fieldInfo + + // InlineMap is the number of the field in the struct that + // contains an ,inline map, or -1 if there's none. + InlineMap int + + // InlineUnmarshalers holds indexes to inlined fields that + // contain unmarshaler values. + InlineUnmarshalers [][]int +} + +type fieldInfo struct { + Key string + Num int + OmitEmpty bool + Flow bool + // Id holds the unique field identifier, so we can cheaply + // check for field duplicates without maintaining an extra map. + Id int + + // Inline holds the field index if the field is part of an inlined struct. + Inline []int +} + +var structMap = make(map[reflect.Type]*structInfo) +var fieldMapMutex sync.RWMutex +var unmarshalerType reflect.Type + +func init() { + var v Unmarshaler + unmarshalerType = reflect.ValueOf(&v).Elem().Type() +} + +func getStructInfo(st reflect.Type) (*structInfo, error) { + fieldMapMutex.RLock() + sinfo, found := structMap[st] + fieldMapMutex.RUnlock() + if found { + return sinfo, nil + } + + n := st.NumField() + fieldsMap := make(map[string]fieldInfo) + fieldsList := make([]fieldInfo, 0, n) + inlineMap := -1 + inlineUnmarshalers := [][]int(nil) + for i := 0; i != n; i++ { + field := st.Field(i) + if field.PkgPath != "" && !field.Anonymous { + continue // Private field + } + + info := fieldInfo{Num: i} + + tag := field.Tag.Get("yaml") + if tag == "" && strings.Index(string(field.Tag), ":") < 0 { + tag = string(field.Tag) + } + if tag == "-" { + continue + } + + inline := false + fields := strings.Split(tag, ",") + if len(fields) > 1 { + for _, flag := range fields[1:] { + switch flag { + case "omitempty": + info.OmitEmpty = true + case "flow": + info.Flow = true + case "inline": + inline = true + default: + return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) + } + } + tag = fields[0] + } + + if inline { + switch field.Type.Kind() { + case reflect.Map: + if inlineMap >= 0 { + return nil, errors.New("multiple ,inline maps in struct " + st.String()) + } + if field.Type.Key() != reflect.TypeOf("") { + return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) + } + inlineMap = info.Num + case reflect.Struct, reflect.Ptr: + ftype := field.Type + for ftype.Kind() == reflect.Ptr { + ftype = ftype.Elem() + } + if ftype.Kind() != reflect.Struct { + return nil, errors.New("option ,inline may only be used on a struct or map field") + } + if reflect.PtrTo(ftype).Implements(unmarshalerType) { + inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) + } else { + sinfo, err := getStructInfo(ftype) + if err != nil { + return nil, err + } + for _, index := range sinfo.InlineUnmarshalers { + inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) + } + for _, finfo := range sinfo.FieldsList { + if _, found := fieldsMap[finfo.Key]; found { + msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + if finfo.Inline == nil { + finfo.Inline = []int{i, finfo.Num} + } else { + finfo.Inline = append([]int{i}, finfo.Inline...) + } + finfo.Id = len(fieldsList) + fieldsMap[finfo.Key] = finfo + fieldsList = append(fieldsList, finfo) + } + } + default: + return nil, errors.New("option ,inline may only be used on a struct or map field") + } + continue + } + + if tag != "" { + info.Key = tag + } else { + info.Key = strings.ToLower(field.Name) + } + + if _, found = fieldsMap[info.Key]; found { + msg := "duplicated key '" + info.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + + info.Id = len(fieldsList) + fieldsList = append(fieldsList, info) + fieldsMap[info.Key] = info + } + + sinfo = &structInfo{ + FieldsMap: fieldsMap, + FieldsList: fieldsList, + InlineMap: inlineMap, + InlineUnmarshalers: inlineUnmarshalers, + } + + fieldMapMutex.Lock() + structMap[st] = sinfo + fieldMapMutex.Unlock() + return sinfo, nil +} + +// IsZeroer is used to check whether an object is zero to +// determine whether it should be omitted when marshaling +// with the omitempty flag. One notable implementation +// is time.Time. +type IsZeroer interface { + IsZero() bool +} + +func isZero(v reflect.Value) bool { + kind := v.Kind() + if z, ok := v.Interface().(IsZeroer); ok { + if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { + return true + } + return z.IsZero() + } + switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // Private field + } + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} diff --git a/vendor/gopkg.in/yaml.v2/yamlh.go b/vendor/go.yaml.in/yaml/v3/yamlh.go similarity index 88% rename from vendor/gopkg.in/yaml.v2/yamlh.go rename to vendor/go.yaml.in/yaml/v3/yamlh.go index f6a9c8e34..f59aa40f6 100644 --- a/vendor/gopkg.in/yaml.v2/yamlh.go +++ b/vendor/go.yaml.in/yaml/v3/yamlh.go @@ -1,3 +1,25 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + package yaml import ( @@ -73,13 +95,13 @@ type yaml_scalar_style_t yaml_style_t // Scalar styles. const ( // Let the emitter choose the style. - yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota + yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0 - yaml_PLAIN_SCALAR_STYLE // The plain scalar style. - yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. - yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. - yaml_LITERAL_SCALAR_STYLE // The literal scalar style. - yaml_FOLDED_SCALAR_STYLE // The folded scalar style. + yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style. + yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. + yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. + yaml_LITERAL_SCALAR_STYLE // The literal scalar style. + yaml_FOLDED_SCALAR_STYLE // The folded scalar style. ) type yaml_sequence_style_t yaml_style_t @@ -238,6 +260,7 @@ const ( yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. yaml_MAPPING_START_EVENT // A MAPPING-START event. yaml_MAPPING_END_EVENT // A MAPPING-END event. + yaml_TAIL_COMMENT_EVENT ) var eventStrings = []string{ @@ -252,6 +275,7 @@ var eventStrings = []string{ yaml_SEQUENCE_END_EVENT: "sequence end", yaml_MAPPING_START_EVENT: "mapping start", yaml_MAPPING_END_EVENT: "mapping end", + yaml_TAIL_COMMENT_EVENT: "tail comment", } func (e yaml_event_type_t) String() string { @@ -279,6 +303,12 @@ type yaml_event_t struct { // The list of tag directives (for yaml_DOCUMENT_START_EVENT). tag_directives []yaml_tag_directive_t + // The comments + head_comment []byte + line_comment []byte + foot_comment []byte + tail_comment []byte + // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). anchor []byte @@ -408,7 +438,9 @@ type yaml_document_t struct { // The number of written bytes should be set to the size_read variable. // // [in,out] data A pointer to an application data specified by -// yaml_parser_set_input(). +// +// yaml_parser_set_input(). +// // [out] buffer The buffer to write the data from the source. // [in] size The size of the buffer. // [out] size_read The actual number of bytes read from the source. @@ -554,6 +586,8 @@ type yaml_parser_t struct { unread int // The number of unread characters in the buffer. + newlines int // The number of line breaks since last non-break/non-blank character + raw_buffer []byte // The raw buffer. raw_buffer_pos int // The current position of the buffer. @@ -562,6 +596,17 @@ type yaml_parser_t struct { offset int // The offset of the current position (in bytes). mark yaml_mark_t // The mark of the current position. + // Comments + + head_comment []byte // The current head comments + line_comment []byte // The current line comments + foot_comment []byte // The current foot comments + tail_comment []byte // Foot comment that happens at the end of a block. + stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc) + + comments []yaml_comment_t // The folded comments for all parsed tokens + comments_head int + // Scanner stuff stream_start_produced bool // Have we started to scan the input stream? @@ -595,6 +640,17 @@ type yaml_parser_t struct { document *yaml_document_t // The currently parsed document. } +type yaml_comment_t struct { + scan_mark yaml_mark_t // Position where scanning for comments started + token_mark yaml_mark_t // Position after which tokens will be associated with this comment + start_mark yaml_mark_t // Position of '#' comment mark + end_mark yaml_mark_t // Position where comment terminated + + head []byte + line []byte + foot []byte +} + // Emitter Definitions // The prototype of a write handler. @@ -604,13 +660,14 @@ type yaml_parser_t struct { // @a buffer to the output. // // @param[in,out] data A pointer to an application data specified by -// yaml_emitter_set_output(). +// +// yaml_emitter_set_output(). +// // @param[in] buffer The buffer with bytes to be written. // @param[in] size The size of the buffer. // // @returns On success, the handler should return @c 1. If the handler failed, // the returned value should be @c 0. -// type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error type yaml_emitter_state_t int @@ -625,8 +682,10 @@ const ( yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. + yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. @@ -685,6 +744,8 @@ type yaml_emitter_t struct { indent int // The current indentation level. + compact_sequence_indent bool // Is '- ' is considered part of the indentation for sequence elements? + flow_level int // The current flow level. root_context bool // Is it the document root context? @@ -698,6 +759,9 @@ type yaml_emitter_t struct { indention bool // If the last character was an indentation character (' ', '-', '?', ':')? open_ended bool // If an explicit document end is required? + space_above bool // Is there's an empty line above? + foot_indent int // The indent used to write the foot comment above, or -1 if none. + // Anchor analysis. anchor_data struct { anchor []byte // The anchor value. @@ -721,6 +785,14 @@ type yaml_emitter_t struct { style yaml_scalar_style_t // The output style. } + // Comments + head_comment []byte + line_comment []byte + foot_comment []byte + tail_comment []byte + + key_line_comment []byte + // Dumper stuff opened bool // If the stream was already opened? diff --git a/vendor/gopkg.in/yaml.v2/yamlprivateh.go b/vendor/go.yaml.in/yaml/v3/yamlprivateh.go similarity index 79% rename from vendor/gopkg.in/yaml.v2/yamlprivateh.go rename to vendor/go.yaml.in/yaml/v3/yamlprivateh.go index 8110ce3c3..dea1ba961 100644 --- a/vendor/gopkg.in/yaml.v2/yamlprivateh.go +++ b/vendor/go.yaml.in/yaml/v3/yamlprivateh.go @@ -1,3 +1,25 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + package yaml const ( @@ -114,7 +136,8 @@ func is_crlf(b []byte, i int) bool { // Check if the character is a line break or NUL. func is_breakz(b []byte, i int) bool { //return is_break(b, i) || is_z(b, i) - return ( // is_break: + return ( + // is_break: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) @@ -127,7 +150,8 @@ func is_breakz(b []byte, i int) bool { // Check if the character is a line break, space, or NUL. func is_spacez(b []byte, i int) bool { //return is_space(b, i) || is_breakz(b, i) - return ( // is_space: + return ( + // is_space: b[i] == ' ' || // is_breakz: b[i] == '\r' || // CR (#xD) @@ -141,7 +165,8 @@ func is_spacez(b []byte, i int) bool { // Check if the character is a line break, space, tab, or NUL. func is_blankz(b []byte, i int) bool { //return is_blank(b, i) || is_breakz(b, i) - return ( // is_blank: + return ( + // is_blank: b[i] == ' ' || b[i] == '\t' || // is_breakz: b[i] == '\r' || // CR (#xD) diff --git a/vendor/golang.org/x/crypto/acme/acme.go b/vendor/golang.org/x/crypto/acme/acme.go index aaafea2bc..b53ea2889 100644 --- a/vendor/golang.org/x/crypto/acme/acme.go +++ b/vendor/golang.org/x/crypto/acme/acme.go @@ -31,12 +31,11 @@ import ( "crypto/x509/pkix" "encoding/asn1" "encoding/base64" - "encoding/hex" "encoding/json" - "encoding/pem" "errors" "fmt" "math/big" + "net" "net/http" "strings" "sync" @@ -353,6 +352,10 @@ func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization if _, err := c.Discover(ctx); err != nil { return nil, err } + if c.dir.AuthzURL == "" { + // Pre-Authorization is unsupported + return nil, errPreAuthorizationNotSupported + } type authzID struct { Type string `json:"type"` @@ -467,7 +470,7 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat // while waiting for a final authorization status. d := retryAfter(res.Header.Get("Retry-After")) if d == 0 { - // Given that the fastest challenges TLS-SNI and HTTP-01 + // Given that the fastest challenges TLS-ALPN and HTTP-01 // require a CA to make at least 1 network round trip // and most likely persist a challenge state, // this default delay seems reasonable. @@ -514,7 +517,11 @@ func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error return nil, err } - res, err := c.post(ctx, nil, chal.URI, json.RawMessage("{}"), wantStatus( + payload := json.RawMessage("{}") + if len(chal.Payload) != 0 { + payload = chal.Payload + } + res, err := c.post(ctx, nil, chal.URI, payload, wantStatus( http.StatusOK, // according to the spec http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md) )) @@ -564,50 +571,28 @@ func (c *Client) HTTP01ChallengePath(token string) string { } // TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response. +// Always returns an error. // -// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec. -func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) { - ka, err := keyAuth(c.Key.Public(), token) - if err != nil { - return tls.Certificate{}, "", err - } - b := sha256.Sum256([]byte(ka)) - h := hex.EncodeToString(b[:]) - name = fmt.Sprintf("%s.%s.acme.invalid", h[:32], h[32:]) - cert, err = tlsChallengeCert([]string{name}, opt) - if err != nil { - return tls.Certificate{}, "", err - } - return cert, name, nil +// Deprecated: This challenge type was only present in pre-standardized ACME +// protocol drafts and is insecure for use in shared hosting environments. +func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (tls.Certificate, string, error) { + return tls.Certificate{}, "", errPreRFC } // TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response. +// Always returns an error. // -// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec. -func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) { - b := sha256.Sum256([]byte(token)) - h := hex.EncodeToString(b[:]) - sanA := fmt.Sprintf("%s.%s.token.acme.invalid", h[:32], h[32:]) - - ka, err := keyAuth(c.Key.Public(), token) - if err != nil { - return tls.Certificate{}, "", err - } - b = sha256.Sum256([]byte(ka)) - h = hex.EncodeToString(b[:]) - sanB := fmt.Sprintf("%s.%s.ka.acme.invalid", h[:32], h[32:]) - - cert, err = tlsChallengeCert([]string{sanA, sanB}, opt) - if err != nil { - return tls.Certificate{}, "", err - } - return cert, sanA, nil +// Deprecated: This challenge type was only present in pre-standardized ACME +// protocol drafts and is insecure for use in shared hosting environments. +func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (tls.Certificate, string, error) { + return tls.Certificate{}, "", errPreRFC } // TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response. // Servers can present the certificate to validate the challenge and prove control -// over a domain name. For more details on TLS-ALPN-01 see -// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3 +// over an identifier (either a DNS name or the textual form of an IPv4 or IPv6 +// address). For more details on TLS-ALPN-01 see +// https://www.rfc-editor.org/rfc/rfc8737 and https://www.rfc-editor.org/rfc/rfc8738 // // The token argument is a Challenge.Token value. // If a WithKey option is provided, its private part signs the returned cert, @@ -615,9 +600,13 @@ func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tl // If no WithKey option is provided, a new ECDSA key is generated using P-256 curve. // // The returned certificate is valid for the next 24 hours and must be presented only when -// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol +// the server name in the TLS ClientHello matches the identifier, and the special acme-tls/1 ALPN protocol // has been specified. -func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption) (cert tls.Certificate, err error) { +// +// Validation requests for IP address identifiers will use the reverse DNS form in the server name +// in the TLS ClientHello since the SNI extension is not supported for IP addresses. +// See RFC 8738 Section 6 for more information. +func (c *Client) TLSALPN01ChallengeCert(token, identifier string, opt ...CertOption) (cert tls.Certificate, err error) { ka, err := keyAuth(c.Key.Public(), token) if err != nil { return tls.Certificate{}, err @@ -647,7 +636,7 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption) } tmpl.ExtraExtensions = append(tmpl.ExtraExtensions, acmeExtension) newOpt = append(newOpt, WithTemplate(tmpl)) - return tlsChallengeCert([]string{domain}, newOpt) + return tlsChallengeCert(identifier, newOpt) } // popNonce returns a nonce value previously stored with c.addNonce @@ -701,7 +690,7 @@ func (c *Client) addNonce(h http.Header) { } func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) { - r, err := http.NewRequest("HEAD", url, nil) + r, err := http.NewRequestWithContext(ctx, "HEAD", url, nil) if err != nil { return "", err } @@ -765,11 +754,15 @@ func defaultTLSChallengeCertTemplate() *x509.Certificate { } } -// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges -// with the given SANs and auto-generated public/private key pair. -// The Subject Common Name is set to the first SAN to aid debugging. +// tlsChallengeCert creates a temporary certificate for TLS-ALPN challenges +// for the given identifier, using an auto-generated public/private key pair. +// +// If the provided identifier is a domain name, it will be used as a DNS type SAN and for the +// subject common name. If the provided identifier is an IP address it will be used as an IP type +// SAN. +// // To create a cert with a custom key pair, specify WithKey option. -func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) { +func tlsChallengeCert(identifier string, opt []CertOption) (tls.Certificate, error) { var key crypto.Signer tmpl := defaultTLSChallengeCertTemplate() for _, o := range opt { @@ -793,9 +786,12 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) { return tls.Certificate{}, err } } - tmpl.DNSNames = san - if len(san) > 0 { - tmpl.Subject.CommonName = san[0] + + if ip := net.ParseIP(identifier); ip != nil { + tmpl.IPAddresses = []net.IP{ip} + } else { + tmpl.DNSNames = []string{identifier} + tmpl.Subject.CommonName = identifier } der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key) @@ -808,11 +804,5 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) { }, nil } -// encodePEM returns b encoded as PEM with block of type typ. -func encodePEM(typ string, b []byte) []byte { - pb := &pem.Block{Type: typ, Bytes: b} - return pem.EncodeToMemory(pb) -} - // timeNow is time.Now, except in tests which can mess with it. var timeNow = time.Now diff --git a/vendor/golang.org/x/crypto/acme/autocert/autocert.go b/vendor/golang.org/x/crypto/acme/autocert/autocert.go index 6b4cdf406..bee6de9ce 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/autocert.go +++ b/vendor/golang.org/x/crypto/acme/autocert/autocert.go @@ -134,7 +134,8 @@ type Manager struct { // RenewBefore optionally specifies how early certificates should // be renewed before they expire. // - // If zero, they're renewed 30 days before expiration. + // If zero, they're renewed at the lesser of 30 days or + // 1/3 of the certificate lifetime. RenewBefore time.Duration // Client is used to perform low-level operations, such as account registration @@ -247,10 +248,6 @@ func (m *Manager) TLSConfig() *tls.Config { // If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will // also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler for http-01. func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { - if m.Prompt == nil { - return nil, errors.New("acme/autocert: Manager.Prompt not set") - } - name := hello.ServerName if name == "" { return nil, errors.New("acme/autocert: missing server name") @@ -292,6 +289,10 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, } // regular domain + if err := m.hostPolicy()(ctx, name); err != nil { + return nil, err + } + ck := certKey{ domain: strings.TrimSuffix(name, "."), // golang.org/issue/18114 isRSA: !supportsECDSA(hello), @@ -305,9 +306,6 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, } // first-time - if err := m.hostPolicy()(ctx, name); err != nil { - return nil, err - } cert, err = m.createCert(ctx, ck) if err != nil { return nil, err @@ -463,7 +461,7 @@ func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error leaf: cert.Leaf, } m.state[ck] = s - m.startRenew(ck, s.key, s.leaf.NotAfter) + m.startRenew(ck, s.key, s.leaf.NotBefore, s.leaf.NotAfter) return cert, nil } @@ -566,24 +564,21 @@ func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error { // If the domain is already being verified, it waits for the existing verification to complete. // Either way, createCert blocks for the duration of the whole process. func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, error) { - // TODO: maybe rewrite this whole piece using sync.Once - state, err := m.certState(ck) + state, owner, err := m.certState(ck) if err != nil { return nil, err } - // state may exist if another goroutine is already working on it - // in which case just wait for it to finish - if !state.locked { + // If another goroutine is already working on this state, wait for it + // to finish by taking the read lock + if !owner { state.RLock() defer state.RUnlock() return state.tlscert() } - // We are the first; state is locked. - // Unblock the readers when domain ownership is verified - // and we got the cert or the process failed. + // We are the first to work on this certKey, so state is write-locked. + // Unblock the readers when our work is complete. defer state.Unlock() - state.locked = false der, leaf, err := m.authorizedCert(ctx, state.key, ck) if err != nil { @@ -609,14 +604,19 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, } state.cert = der state.leaf = leaf - m.startRenew(ck, state.key, state.leaf.NotAfter) + m.startRenew(ck, state.key, state.leaf.NotBefore, state.leaf.NotAfter) return state.tlscert() } -// certState returns a new or existing certState. -// If a new certState is returned, state.exist is false and the state is locked. +// certState returns a new or existing certState along with a boolean +// indicating whether the caller is the owner of the state. +// +// The owner of the state is responsible for performing the ACME work and +// must unlock the state's write lock when done. Non-owner callers should +// wait on the state's read lock for the owner to finish. +// // The returned error is non-nil only in the case where a new state could not be created. -func (m *Manager) certState(ck certKey) (*certState, error) { +func (m *Manager) certState(ck certKey) (*certState, bool, error) { m.stateMu.Lock() defer m.stateMu.Unlock() if m.state == nil { @@ -624,7 +624,7 @@ func (m *Manager) certState(ck certKey) (*certState, error) { } // existing state if state, ok := m.state[ck]; ok { - return state, nil + return state, false, nil } // new locked state @@ -638,16 +638,13 @@ func (m *Manager) certState(ck certKey) (*certState, error) { key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } if err != nil { - return nil, err + return nil, false, err } - state := &certState{ - key: key, - locked: true, - } + state := &certState{key: key} state.Lock() // will be unlocked by m.certState caller m.state[ck] = state - return state, nil + return state, true, nil } // authorizedCert starts the domain ownership verification process and requests a new cert upon success. @@ -907,7 +904,7 @@ func httpTokenCacheKey(tokenPath string) string { // // The key argument is a certificate private key. // The exp argument is the cert expiration time (NotAfter). -func (m *Manager) startRenew(ck certKey, key crypto.Signer, exp time.Time) { +func (m *Manager) startRenew(ck certKey, key crypto.Signer, notBefore, notAfter time.Time) { m.renewalMu.Lock() defer m.renewalMu.Unlock() if m.renewal[ck] != nil { @@ -919,7 +916,7 @@ func (m *Manager) startRenew(ck certKey, key crypto.Signer, exp time.Time) { } dr := &domainRenewal{m: m, ck: ck, key: key} m.renewal[ck] = dr - dr.start(exp) + dr.start(notBefore, notAfter) } // stopRenew stops all currently running cert renewal timers. @@ -1027,13 +1024,6 @@ func (m *Manager) hostPolicy() HostPolicy { return defaultHostPolicy } -func (m *Manager) renewBefore() time.Duration { - if m.RenewBefore > renewJitter { - return m.RenewBefore - } - return 720 * time.Hour // 30 days -} - func (m *Manager) now() time.Time { if m.nowFunc != nil { return m.nowFunc() @@ -1044,10 +1034,9 @@ func (m *Manager) now() time.Time { // certState is ready when its mutex is unlocked for reading. type certState struct { sync.RWMutex - locked bool // locked for read/write - key crypto.Signer // private key for cert - cert [][]byte // DER encoding - leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil + key crypto.Signer // private key for cert + cert [][]byte // DER encoding + leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil } // tlscert creates a tls.Certificate from s.key and s.cert. diff --git a/vendor/golang.org/x/crypto/acme/autocert/listener.go b/vendor/golang.org/x/crypto/acme/autocert/listener.go index 9d62f8ced..460133e0c 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/listener.go +++ b/vendor/golang.org/x/crypto/acme/autocert/listener.go @@ -10,7 +10,6 @@ import ( "net" "os" "path/filepath" - "runtime" "time" ) @@ -124,32 +123,13 @@ func (ln *listener) Close() error { return ln.tcpListener.Close() } -func homeDir() string { - if runtime.GOOS == "windows" { - return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") - } - if h := os.Getenv("HOME"); h != "" { - return h - } - return "/" -} - func cacheDir() string { const base = "golang-autocert" - switch runtime.GOOS { - case "darwin": - return filepath.Join(homeDir(), "Library", "Caches", base) - case "windows": - for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} { - if v := os.Getenv(ev); v != "" { - return filepath.Join(v, base) - } - } - // Worst case: - return filepath.Join(homeDir(), base) - } - if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" { - return filepath.Join(xdg, base) + cache, err := os.UserCacheDir() + if err != nil { + // Fall back to the root directory. + cache = "/.cache" } - return filepath.Join(homeDir(), ".cache", base) + + return filepath.Join(cache, base) } diff --git a/vendor/golang.org/x/crypto/acme/autocert/renewal.go b/vendor/golang.org/x/crypto/acme/autocert/renewal.go index 0df7da78a..93984f386 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/renewal.go +++ b/vendor/golang.org/x/crypto/acme/autocert/renewal.go @@ -11,9 +11,6 @@ import ( "time" ) -// renewJitter is the maximum deviation from Manager.RenewBefore. -const renewJitter = time.Hour - // domainRenewal tracks the state used by the periodic timers // renewing a single domain's cert. type domainRenewal struct { @@ -30,13 +27,13 @@ type domainRenewal struct { // defined by the certificate expiration time exp. // // If the timer is already started, calling start is a noop. -func (dr *domainRenewal) start(exp time.Time) { +func (dr *domainRenewal) start(notBefore, notAfter time.Time) { dr.timerMu.Lock() defer dr.timerMu.Unlock() if dr.timer != nil { return } - dr.timer = time.AfterFunc(dr.next(exp), dr.renew) + dr.timer = time.AfterFunc(dr.next(notBefore, notAfter), dr.renew) } // stop stops the cert renewal timer and waits for any in-flight calls to renew @@ -79,7 +76,7 @@ func (dr *domainRenewal) renew() { // TODO: rotate dr.key at some point? next, err := dr.do(ctx) if err != nil { - next = renewJitter / 2 + next = time.Hour / 2 next += time.Duration(pseudoRand.int63n(int64(next))) } testDidRenewLoop(next, err) @@ -107,8 +104,8 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { // a race is likely unavoidable in a distributed environment // but we try nonetheless if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil { - next := dr.next(tlscert.Leaf.NotAfter) - if next > dr.m.renewBefore()+renewJitter { + next := dr.next(tlscert.Leaf.NotBefore, tlscert.Leaf.NotAfter) + if next > 0 { signer, ok := tlscert.PrivateKey.(crypto.Signer) if ok { state := &certState{ @@ -139,18 +136,23 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { return 0, err } dr.updateState(state) - return dr.next(leaf.NotAfter), nil + return dr.next(leaf.NotBefore, leaf.NotAfter), nil } -func (dr *domainRenewal) next(expiry time.Time) time.Duration { - d := expiry.Sub(dr.m.now()) - dr.m.renewBefore() - // add a bit of randomness to renew deadline - n := pseudoRand.int63n(int64(renewJitter)) - d -= time.Duration(n) - if d < 0 { - return 0 +// next returns the wait time before the next renewal should start. +// If manager.RenewBefore is set, it uses that capped at 30 days, +// otherwise it uses a default of 1/3 of the cert lifetime. +// It builds in a jitter of 10% of the renew threshold, capped at 1 hour. +func (dr *domainRenewal) next(notBefore, notAfter time.Time) time.Duration { + threshold := min(notAfter.Sub(notBefore)/3, 30*24*time.Hour) + if dr.m.RenewBefore > 0 { + threshold = min(dr.m.RenewBefore, 30*24*time.Hour) } - return d + maxJitter := min(threshold/10, time.Hour) + jitter := pseudoRand.int63n(int64(maxJitter)) + renewAt := notAfter.Add(-(threshold - time.Duration(jitter))) + renewWait := renewAt.Sub(dr.m.now()) + return max(0, renewWait) } var testDidRenewLoop = func(next time.Duration, err error) {} diff --git a/vendor/golang.org/x/crypto/acme/http.go b/vendor/golang.org/x/crypto/acme/http.go index d92ff232f..7d1052acd 100644 --- a/vendor/golang.org/x/crypto/acme/http.go +++ b/vendor/golang.org/x/crypto/acme/http.go @@ -66,7 +66,7 @@ func (c *Client) retryTimer() *retryTimer { // The n argument is always bounded between 1 and 30. // The returned value is always greater than 0. func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration { - const max = 10 * time.Second + const maxVal = 10 * time.Second var jitter time.Duration if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil { // Set the minimum to 1ms to avoid a case where @@ -86,10 +86,7 @@ func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration { n = 30 } d := time.Duration(1< max { - return max - } - return d + return min(d, maxVal) } // retryAfter parses a Retry-After HTTP header value, @@ -131,7 +128,7 @@ func wantStatus(codes ...int) resOkay { func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) { retry := c.retryTimer() for { - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, err } @@ -231,7 +228,7 @@ func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, if err != nil { return nil, nil, err } - req, err := http.NewRequest("POST", url, bytes.NewReader(b)) + req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(b)) if err != nil { return nil, nil, err } diff --git a/vendor/golang.org/x/crypto/acme/jws.go b/vendor/golang.org/x/crypto/acme/jws.go index b38828d85..685027566 100644 --- a/vendor/golang.org/x/crypto/acme/jws.go +++ b/vendor/golang.org/x/crypto/acme/jws.go @@ -92,7 +92,7 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid KeyID, nonce, ur if err != nil { return nil, err } - phead := base64.RawURLEncoding.EncodeToString([]byte(phJSON)) + phead := base64.RawURLEncoding.EncodeToString(phJSON) var payload string if val, ok := claimset.(string); ok { payload = val diff --git a/vendor/golang.org/x/crypto/acme/rfc8555.go b/vendor/golang.org/x/crypto/acme/rfc8555.go index 3152e531b..1fb110e08 100644 --- a/vendor/golang.org/x/crypto/acme/rfc8555.go +++ b/vendor/golang.org/x/crypto/acme/rfc8555.go @@ -53,6 +53,9 @@ func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tos Contact: acct.Contact, } if c.dir.Terms != "" { + if prompt == nil { + return nil, errors.New("acme: missing Manager.Prompt to accept server's terms of service") + } req.TermsAgreed = prompt(c.dir.Terms) } @@ -232,7 +235,7 @@ func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderO return responseOrder(res) } -// GetOrder retrives an order identified by the given URL. +// GetOrder retrieves an order identified by the given URL. // For orders created with AuthorizeOrder, the url value is Order.URI. // // If a caller needs to poll an order until its status is final, @@ -272,7 +275,7 @@ func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) { case err != nil: // Skip and retry. case o.Status == StatusInvalid: - return nil, &OrderError{OrderURL: o.URI, Status: o.Status} + return nil, &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error} case o.Status == StatusReady || o.Status == StatusValid: return o, nil } @@ -369,7 +372,7 @@ func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bu } // The only acceptable status post finalize and WaitOrder is "valid". if o.Status != StatusValid { - return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status} + return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error} } crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle) return crt, o.CertURL, err diff --git a/vendor/golang.org/x/crypto/acme/types.go b/vendor/golang.org/x/crypto/acme/types.go index 23a4d6517..65d69b261 100644 --- a/vendor/golang.org/x/crypto/acme/types.go +++ b/vendor/golang.org/x/crypto/acme/types.go @@ -7,6 +7,7 @@ package acme import ( "crypto" "crypto/x509" + "encoding/json" "errors" "fmt" "net/http" @@ -55,6 +56,10 @@ var ( // ErrNoAccount indicates that the Client's key has not been registered with the CA. ErrNoAccount = errors.New("acme: account does not exist") + + // errPreAuthorizationNotSupported indicates that the server does not + // support pre-authorization of identifiers. + errPreAuthorizationNotSupported = errors.New("acme: pre-authorization is not supported") ) // A Subproblem describes an ACME subproblem as reported in an Error. @@ -149,17 +154,24 @@ func (a *AuthorizationError) Error() string { // OrderError is returned from Client's order related methods. // It indicates the order is unusable and the clients should start over with -// AuthorizeOrder. +// AuthorizeOrder. A Problem description may be provided with details on +// what caused the order to become unusable. // // The clients can still fetch the order object from CA using GetOrder // to inspect its state. type OrderError struct { OrderURL string Status string + // Problem is the error that occurred while processing the order. + Problem *Error } func (oe *OrderError) Error() string { - return fmt.Sprintf("acme: order %s status: %s", oe.OrderURL, oe.Status) + str := fmt.Sprintf("acme: order %s status: %s", oe.OrderURL, oe.Status) + if oe.Problem != nil { + str += fmt.Sprintf("; problem: %s", oe.Problem) + } + return str } // RateLimit reports whether err represents a rate limit error and @@ -527,6 +539,16 @@ type Challenge struct { // when this challenge was used. // The type of a non-nil value is *Error. Error error + + // Payload is the JSON-formatted payload that the client sends + // to the server to indicate it is ready to respond to the challenge. + // When unset, it defaults to an empty JSON object: {}. + // For most challenges, the client must not set Payload, + // see https://tools.ietf.org/html/rfc8555#section-7.5.1. + // Payload is used only for newer challenges (such as "device-attest-01") + // where the client must send additional data for the server to validate + // the challenge. + Payload json.RawMessage } // wireChallenge is ACME JSON challenge representation. @@ -604,7 +626,7 @@ func (*certOptKey) privateCertOpt() {} // // In TLS ChallengeCert methods, the template is also used as parent, // resulting in a self-signed certificate. -// The DNSNames field of t is always overwritten for tls-sni challenge certs. +// The DNSNames or IPAddresses fields of t are always overwritten for tls-alpn challenge certs. func WithTemplate(t *x509.Certificate) CertOption { return (*certOptTemplate)(t) } diff --git a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go index 28cd99c7f..b33212203 100644 --- a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go +++ b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go @@ -2,24 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -/* -Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC -2898 / PKCS #5 v2.0. - -A key derivation function is useful when encrypting data based on a password -or any other not-fully-random data. It uses a pseudorandom function to derive -a secure encryption key based on the password. - -While v2.0 of the standard defines only one pseudorandom function to use, -HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved -Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To -choose, you can pass the `New` functions from the different SHA packages to -pbkdf2.Key. -*/ +// Package pbkdf2 implements the key derivation function PBKDF2 as defined in +// RFC 8018 (PKCS #5 v2.1). +// +// This package is a wrapper for the PBKDF2 implementation in the +// [crypto/pbkdf2] package. It is [frozen] and is not accepting new features. +// +// [frozen]: https://go.dev/wiki/Frozen package pbkdf2 import ( - "crypto/hmac" + "crypto/pbkdf2" "hash" ) @@ -27,51 +20,11 @@ import ( // []byte of length keylen that can be used as cryptographic key. The key is // derived based on the method described as PBKDF2 with the HMAC variant using // the supplied hash function. -// -// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you -// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by -// doing: -// -// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) -// -// Remember to get a good random salt. At least 8 bytes is recommended by the -// RFC. -// -// Using a higher iteration count will increase the cost of an exhaustive -// search but will also make derivation proportionally slower. func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { - prf := hmac.New(h, password) - hashLen := prf.Size() - numBlocks := (keyLen + hashLen - 1) / hashLen - - var buf [4]byte - dk := make([]byte, 0, numBlocks*hashLen) - U := make([]byte, hashLen) - for block := 1; block <= numBlocks; block++ { - // N.B.: || means concatenation, ^ means XOR - // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter - // U_1 = PRF(password, salt || uint(i)) - prf.Reset() - prf.Write(salt) - buf[0] = byte(block >> 24) - buf[1] = byte(block >> 16) - buf[2] = byte(block >> 8) - buf[3] = byte(block) - prf.Write(buf[:4]) - dk = prf.Sum(dk) - T := dk[len(dk)-hashLen:] - copy(U, T) - - // U_n = PRF(password, U_(n-1)) - for n := 2; n <= iter; n++ { - prf.Reset() - prf.Write(U) - U = U[:0] - U = prf.Sum(U) - for x := range U { - T[x] ^= U[x] - } - } + out, err := pbkdf2.Key(h, string(password), salt, iter, keyLen) + if err != nil { + // FIPS 140 enforcement, or an invalid key length. + panic(err) } - return dk[:keyLen] + return out } diff --git a/vendor/golang.org/x/crypto/scrypt/scrypt.go b/vendor/golang.org/x/crypto/scrypt/scrypt.go index 76fa40fb2..b422b7d7f 100644 --- a/vendor/golang.org/x/crypto/scrypt/scrypt.go +++ b/vendor/golang.org/x/crypto/scrypt/scrypt.go @@ -196,6 +196,9 @@ func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) { if N <= 1 || N&(N-1) != 0 { return nil, errors.New("scrypt: N must be > 1 and a power of 2") } + if r <= 0 || p <= 0 { + return nil, errors.New("scrypt: parameters must be > 0") + } if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r { return nil, errors.New("scrypt: parameters are too large") } diff --git a/vendor/golang.org/x/mod/modfile/print.go b/vendor/golang.org/x/mod/modfile/print.go index 2a0123d4b..48dbd82ae 100644 --- a/vendor/golang.org/x/mod/modfile/print.go +++ b/vendor/golang.org/x/mod/modfile/print.go @@ -33,7 +33,7 @@ type printer struct { } // printf prints to the buffer. -func (p *printer) printf(format string, args ...interface{}) { +func (p *printer) printf(format string, args ...any) { fmt.Fprintf(p, format, args...) } diff --git a/vendor/golang.org/x/mod/modfile/read.go b/vendor/golang.org/x/mod/modfile/read.go index de1b98211..9e35e1ac5 100644 --- a/vendor/golang.org/x/mod/modfile/read.go +++ b/vendor/golang.org/x/mod/modfile/read.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "os" + "slices" "strconv" "strings" "unicode" @@ -94,7 +95,7 @@ func (x *FileSyntax) Span() (start, end Position) { // line, the new line is added at the end of the block containing hint, // extracting hint into a new block if it is not yet in one. // -// If the hint is non-nil buts its first token does not match, +// If the hint is non-nil but its first token does not match, // the new line is added after the block containing hint // (or hint itself, if not in a block). // @@ -105,8 +106,7 @@ func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line { if hint == nil { // If no hint given, add to the last statement of the given type. Loop: - for i := len(x.Stmt) - 1; i >= 0; i-- { - stmt := x.Stmt[i] + for _, stmt := range slices.Backward(x.Stmt) { switch stmt := stmt.(type) { case *Line: if stmt.Token != nil && stmt.Token[0] == tokens[0] { @@ -600,7 +600,7 @@ func (in *input) readToken() { // Checked all punctuation. Must be identifier token. if c := in.peekRune(); !isIdent(c) { - in.Error(fmt.Sprintf("unexpected input character %#q", c)) + in.Error(fmt.Sprintf("unexpected input character %#q", rune(c))) } // Scan over identifier. @@ -718,9 +718,7 @@ func (in *input) assignComments() { } // Assign suffix comments to syntax immediately before. - for i := len(in.post) - 1; i >= 0; i-- { - x := in.post[i] - + for _, x := range slices.Backward(in.post) { start, end := x.Span() if debug { fmt.Fprintf(os.Stderr, "post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte) @@ -877,6 +875,11 @@ func (in *input) parseLineBlock(start Position, token []string, lparen token) *L in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune)) case ')': rparen := in.lex() + // Don't preserve blank lines (denoted by a single empty comment, added above) + // at the end of the block. + if len(comments) == 1 && comments[0] == (Comment{}) { + comments = nil + } x.RParen.Before = comments x.RParen.Pos = rparen.pos if !in.peek().isEOL() { @@ -921,7 +924,7 @@ var ( moduleStr = []byte("module") ) -// ModulePath returns the module path from the gomod file text. +// ModulePath returns the module path from the go.mod file text. // If it cannot find a module path, it returns an empty string. // It is tolerant of unrelated problems in the go.mod file. func ModulePath(mod []byte) string { diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go index 3e4a1d0ab..20ba825d2 100644 --- a/vendor/golang.org/x/mod/modfile/rule.go +++ b/vendor/golang.org/x/mod/modfile/rule.go @@ -20,10 +20,11 @@ package modfile import ( + "cmp" "errors" "fmt" "path/filepath" - "sort" + "slices" "strconv" "strings" "unicode" @@ -44,6 +45,7 @@ type File struct { Replace []*Replace Retract []*Retract Tool []*Tool + Ignore []*Ignore Syntax *FileSyntax } @@ -100,6 +102,12 @@ type Tool struct { Syntax *Line } +// An Ignore is a single ignore statement. +type Ignore struct { + Path string + Syntax *Line +} + // A VersionInterval represents a range of versions with upper and lower bounds. // Intervals are closed: both bounds are included. When Low is equal to High, // the interval may refer to a single version ('v1.2.3') or an interval @@ -304,7 +312,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse }) } continue - case "module", "godebug", "require", "exclude", "replace", "retract", "tool": + case "module", "godebug", "require", "exclude", "replace", "retract", "tool", "ignore": for _, l := range x.Line { f.add(&errs, x, l, x.Token[0], l.Token, fix, strict) } @@ -319,6 +327,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse } var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?([a-z]+[0-9]+)?$`) + var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].*)$`) // Toolchains must be named beginning with `go1`, @@ -337,7 +346,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a // and simply ignore those statements. if !strict { switch verb { - case "go", "module", "retract", "require": + case "go", "module", "retract", "require", "ignore": // want these even for dependency go.mods default: return @@ -360,7 +369,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a Err: err, }) } - errorf := func(format string, args ...interface{}) { + errorf := func(format string, args ...any) { wrapError(fmt.Errorf(format, args...)) } @@ -531,6 +540,21 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a Path: s, Syntax: line, }) + + case "ignore": + if len(args) != 1 { + errorf("ignore directive expects exactly one argument") + return + } + s, err := parseString(&args[0]) + if err != nil { + errorf("invalid quoted string: %v", err) + return + } + f.Ignore = append(f.Ignore, &Ignore{ + Path: s, + Syntax: line, + }) } } @@ -551,7 +575,7 @@ func parseReplace(filename string, line *Line, verb string, args []string, fix V Err: err, } } - errorf := func(format string, args ...interface{}) *Error { + errorf := func(format string, args ...any) *Error { return wrapError(fmt.Errorf(format, args...)) } @@ -662,7 +686,7 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string, Err: err, }) } - errorf := func(format string, args ...interface{}) { + errorf := func(format string, args ...any) { wrapError(fmt.Errorf(format, args...)) } @@ -1249,6 +1273,17 @@ func (f *File) SetRequire(req []*Require) { // SetRequireSeparateIndirect will split it into a direct-only and indirect-only // block. This aids in the transition to separate blocks. func (f *File) SetRequireSeparateIndirect(req []*Require) { + f.setRequireSeparateIndirect(req, false) +} + +// SetRequireAtMostTwo is like SetRequireSeparateIndirect but it aggressively +// consolidates all requirements into at most two blocks (one direct, one indirect). +// It ignores existing blocks and comments when deciding where to place requirements. +func (f *File) SetRequireAtMostTwo(req []*Require) { + f.setRequireSeparateIndirect(req, true) +} + +func (f *File) setRequireSeparateIndirect(req []*Require, simplify bool) { // hasComments returns whether a line or block has comments // other than "indirect". hasComments := func(c Comments) bool { @@ -1281,6 +1316,17 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) { } // Examine existing require lines and blocks. + need := make(map[string]*Require) + for _, r := range req { + need[r.Mod.Path] = r + } + lineIndirect := make(map[*Line]bool) + for _, r := range f.Require { + if n := need[r.Mod.Path]; n != nil { + lineIndirect[r.Syntax] = n.Indirect + } + } + var ( // We may insert new requirements into the last uncommented // direct-only and indirect-only blocks. We may also move requirements @@ -1298,7 +1344,9 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) { // Track the block each requirement belongs to (if any) so we can // move them later. - lineToBlock = make(map[*Line]*LineBlock) + lineToBlock = make(map[*Line]*LineBlock) + directBlockComments []Comment + indirectBlockComments []Comment ) for i, stmt := range f.Syntax.Stmt { switch stmt := stmt.(type) { @@ -1341,6 +1389,24 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) { if allIndirect { lastIndirectIndex = i } + if simplify { + anyDirect := false + for _, line := range stmt.Line { + if ind, ok := lineIndirect[line]; ok && !ind { + anyDirect = true + break + } + } + target := &directBlockComments + if !anyDirect && len(stmt.Line) > 0 { + target = &indirectBlockComments + } + if len(*target) > 0 && len(stmt.Comments.Before) > 0 { + *target = append(*target, Comment{Token: "//"}) + } + *target = append(*target, stmt.Comments.Before...) + stmt.Comments.Before = nil + } } } @@ -1399,18 +1465,23 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) { lastIndirectBlock = ensureBlock(lastIndirectIndex) } + if simplify { + if len(directBlockComments) > 0 { + lastDirectBlock.Comments.Before = append(lastDirectBlock.Comments.Before, directBlockComments...) + } + if len(indirectBlockComments) > 0 { + lastIndirectBlock.Comments.Before = append(lastIndirectBlock.Comments.Before, indirectBlockComments...) + } + } + // Delete requirements we don't want anymore. // Update versions and indirect comments on requirements we want to keep. // If a requirement is in last{Direct,Indirect}Block with the wrong - // indirect marking after this, or if the requirement is in an single + // indirect marking after this, or if the requirement is in a single // uncommented mixed block (oneFlatUncommentedBlock), move it to the // correct block. // // Some blocks may be empty after this. Cleanup will remove them. - need := make(map[string]*Require) - for _, r := range req { - need[r.Mod.Path] = r - } have := make(map[string]*Require) for _, r := range f.Require { path := r.Mod.Path @@ -1423,10 +1494,10 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) { r.setVersion(need[path].Mod.Version) r.setIndirect(need[path].Indirect) if need[path].Indirect && - (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) { + (simplify || oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) { moveReq(r, lastIndirectBlock) } else if !need[path].Indirect && - (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) { + (simplify || oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) { moveReq(r, lastDirectBlock) } } @@ -1466,7 +1537,7 @@ func (f *File) DropRequire(path string) error { return nil } -// AddExclude adds a exclude statement to the mod file. Errors if the provided +// AddExclude adds an exclude statement to the mod file. Errors if the provided // version is not a canonical version string func (f *File) AddExclude(path, vers string) error { if err := checkCanonicalVersion(path, vers); err != nil { @@ -1571,7 +1642,7 @@ func (f *File) AddRetract(vi VersionInterval, rationale string) error { r.Syntax = f.Syntax.addLine(nil, "retract", "[", AutoQuote(vi.Low), ",", AutoQuote(vi.High), "]") } if rationale != "" { - for _, line := range strings.Split(rationale, "\n") { + for line := range strings.SplitSeq(rationale, "\n") { com := Comment{Token: "// " + line} r.Syntax.Comment().Before = append(r.Syntax.Comment().Before, com) } @@ -1619,6 +1690,36 @@ func (f *File) DropTool(path string) error { return nil } +// AddIgnore adds a new ignore directive with the given path. +// It does nothing if the ignore line already exists. +func (f *File) AddIgnore(path string) error { + for _, t := range f.Ignore { + if t.Path == path { + return nil + } + } + + f.Ignore = append(f.Ignore, &Ignore{ + Path: path, + Syntax: f.Syntax.addLine(nil, "ignore", path), + }) + + f.SortBlocks() + return nil +} + +// DropIgnore removes an ignore directive with the given path. +// It does nothing if no such ignore directive exists. +func (f *File) DropIgnore(path string) error { + for _, t := range f.Ignore { + if t.Path == path { + t.Syntax.markRemoved() + *t = Ignore{} + } + } + return nil +} + func (f *File) SortBlocks() { f.removeDups() // otherwise sorting is unsafe @@ -1633,15 +1734,13 @@ func (f *File) SortBlocks() { if !ok { continue } - less := lineLess + less := compareLine if block.Token[0] == "exclude" && useSemanticSortForExclude { - less = lineExcludeLess + less = compareLineExclude } else if block.Token[0] == "retract" { - less = lineRetractLess + less = compareLineRetract } - sort.SliceStable(block.Line, func(i, j int) bool { - return less(block.Line[i], block.Line[j]) - }) + slices.SortStableFunc(block.Line, less) } } @@ -1657,10 +1756,10 @@ func (f *File) SortBlocks() { // retract directives are not de-duplicated since comments are // meaningful, and versions may be retracted multiple times. func (f *File) removeDups() { - removeDups(f.Syntax, &f.Exclude, &f.Replace, &f.Tool) + removeDups(f.Syntax, &f.Exclude, &f.Replace, &f.Tool, &f.Ignore) } -func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace, tool *[]*Tool) { +func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace, tool *[]*Tool, ignore *[]*Ignore) { kill := make(map[*Line]bool) // Remove duplicate excludes. @@ -1685,8 +1784,7 @@ func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace, to // Remove duplicate replacements. // Later replacements take priority over earlier ones. haveReplace := make(map[module.Version]bool) - for i := len(*replace) - 1; i >= 0; i-- { - x := (*replace)[i] + for _, x := range slices.Backward(*replace) { if haveReplace[x.Old] { kill[x.Syntax] = true continue @@ -1719,6 +1817,24 @@ func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace, to *tool = newTool } + if ignore != nil { + haveIgnore := make(map[string]bool) + for _, i := range *ignore { + if haveIgnore[i.Path] { + kill[i.Syntax] = true + continue + } + haveIgnore[i.Path] = true + } + var newIgnore []*Ignore + for _, i := range *ignore { + if !kill[i.Syntax] { + newIgnore = append(newIgnore, i) + } + } + *ignore = newIgnore + } + // Duplicate require and retract directives are not removed. // Drop killed statements from the syntax tree. @@ -1746,39 +1862,38 @@ func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace, to syntax.Stmt = stmts } -// lineLess returns whether li should be sorted before lj. It sorts -// lexicographically without assigning any special meaning to tokens. -func lineLess(li, lj *Line) bool { +// compareLine compares li and lj. It sorts lexicographically without assigning +// any special meaning to tokens. +func compareLine(li, lj *Line) int { for k := 0; k < len(li.Token) && k < len(lj.Token); k++ { if li.Token[k] != lj.Token[k] { - return li.Token[k] < lj.Token[k] + return cmp.Compare(li.Token[k], lj.Token[k]) } } - return len(li.Token) < len(lj.Token) + return cmp.Compare(len(li.Token), len(lj.Token)) } -// lineExcludeLess reports whether li should be sorted before lj for lines in -// an "exclude" block. -func lineExcludeLess(li, lj *Line) bool { +// compareLineExclude compares li and lj for lines in an "exclude" block. +func compareLineExclude(li, lj *Line) int { if len(li.Token) != 2 || len(lj.Token) != 2 { // Not a known exclude specification. // Fall back to sorting lexicographically. - return lineLess(li, lj) + return compareLine(li, lj) } // An exclude specification has two tokens: ModulePath and Version. // Compare module path by string order and version by semver rules. if pi, pj := li.Token[0], lj.Token[0]; pi != pj { - return pi < pj + return cmp.Compare(pi, pj) } - return semver.Compare(li.Token[1], lj.Token[1]) < 0 + return semver.Compare(li.Token[1], lj.Token[1]) } -// lineRetractLess returns whether li should be sorted before lj for lines in -// a "retract" block. It treats each line as a version interval. Single versions -// are compared as if they were intervals with the same low and high version. +// compareLineRetract compares li and lj for lines in a "retract" block. +// It treats each line as a version interval. Single versions are compared as +// if they were intervals with the same low and high version. // Intervals are sorted in descending order, first by low version, then by -// high version, using semver.Compare. -func lineRetractLess(li, lj *Line) bool { +// high version, using [semver.Compare]. +func compareLineRetract(li, lj *Line) int { interval := func(l *Line) VersionInterval { if len(l.Token) == 1 { return VersionInterval{Low: l.Token[0], High: l.Token[0]} @@ -1792,9 +1907,9 @@ func lineRetractLess(li, lj *Line) bool { vii := interval(li) vij := interval(lj) if cmp := semver.Compare(vii.Low, vij.Low); cmp != 0 { - return cmp > 0 + return -cmp } - return semver.Compare(vii.High, vij.High) > 0 + return -semver.Compare(vii.High, vij.High) } // checkCanonicalVersion returns a non-nil error if vers is not a canonical diff --git a/vendor/golang.org/x/mod/modfile/work.go b/vendor/golang.org/x/mod/modfile/work.go index 5387d0c26..09df5ea3c 100644 --- a/vendor/golang.org/x/mod/modfile/work.go +++ b/vendor/golang.org/x/mod/modfile/work.go @@ -6,7 +6,7 @@ package modfile import ( "fmt" - "sort" + "slices" "strings" ) @@ -315,9 +315,7 @@ func (f *WorkFile) SortBlocks() { if !ok { continue } - sort.SliceStable(block.Line, func(i, j int) bool { - return lineLess(block.Line[i], block.Line[j]) - }) + slices.SortStableFunc(block.Line, compareLine) } } @@ -331,5 +329,5 @@ func (f *WorkFile) SortBlocks() { // retract directives are not de-duplicated since comments are // meaningful, and versions may be retracted multiple times. func (f *WorkFile) removeDups() { - removeDups(f.Syntax, nil, &f.Replace, nil) + removeDups(f.Syntax, nil, &f.Replace, nil, nil) } diff --git a/vendor/golang.org/x/mod/module/module.go b/vendor/golang.org/x/mod/module/module.go index 2a364b229..739c13f48 100644 --- a/vendor/golang.org/x/mod/module/module.go +++ b/vendor/golang.org/x/mod/module/module.go @@ -96,10 +96,11 @@ package module // Changes to the semantics in this file require approval from rsc. import ( + "cmp" "errors" "fmt" "path" - "sort" + "slices" "strings" "unicode" "unicode/utf8" @@ -260,7 +261,7 @@ func modPathOK(r rune) bool { // importPathOK reports whether r can appear in a package import path element. // -// Import paths are intermediate between module paths and file paths: we allow +// Import paths are intermediate between module paths and file paths: we // disallow characters that would be confusing or ambiguous as arguments to // 'go get' (such as '@' and ' ' ), but allow certain characters that are // otherwise-unambiguous on the command line and historically used for some @@ -657,17 +658,15 @@ func CanonicalVersion(v string) string { // optionally followed by a tie-breaking suffix introduced by a slash character, // like in "v0.0.1/go.mod". func Sort(list []Version) { - sort.Slice(list, func(i, j int) bool { - mi := list[i] - mj := list[j] - if mi.Path != mj.Path { - return mi.Path < mj.Path + slices.SortFunc(list, func(i, j Version) int { + if i.Path != j.Path { + return strings.Compare(i.Path, j.Path) } // To help go.sum formatting, allow version/file. // Compare semver prefix by semver rules, // file by string order. - vi := mi.Version - vj := mj.Version + vi := i.Version + vj := j.Version var fi, fj string if k := strings.Index(vi, "/"); k >= 0 { vi, fi = vi[:k], vi[k:] @@ -676,9 +675,9 @@ func Sort(list []Version) { vj, fj = vj[:k], vj[k:] } if vi != vj { - return semver.Compare(vi, vj) < 0 + return semver.Compare(vi, vj) } - return fi < fj + return cmp.Compare(fi, fj) }) } @@ -803,8 +802,8 @@ func MatchPrefixPatterns(globs, target string) bool { for globs != "" { // Extract next non-empty glob in comma-separated list. var glob string - if i := strings.Index(globs, ","); i >= 0 { - glob, globs = globs[:i], globs[i+1:] + if before, after, ok := strings.Cut(globs, ","); ok { + glob, globs = before, after } else { glob, globs = globs, "" } diff --git a/vendor/golang.org/x/mod/semver/semver.go b/vendor/golang.org/x/mod/semver/semver.go index 9a2dfd33a..824b282c8 100644 --- a/vendor/golang.org/x/mod/semver/semver.go +++ b/vendor/golang.org/x/mod/semver/semver.go @@ -22,7 +22,10 @@ // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. package semver -import "sort" +import ( + "slices" + "strings" +) // parsed returns the parsed form of a semantic version string. type parsed struct { @@ -42,8 +45,8 @@ func IsValid(v string) bool { // Canonical returns the canonical formatting of the semantic version v. // It fills in any missing .MINOR or .PATCH and discards build metadata. -// Two semantic versions compare equal only if their canonical formattings -// are identical strings. +// Two semantic versions compare equal only if their canonical formatting +// is an identical string. // The canonical invalid semantic version is the empty string. func Canonical(v string) string { p, ok := parse(v) @@ -154,19 +157,22 @@ func Max(v, w string) string { // ByVersion implements [sort.Interface] for sorting semantic version strings. type ByVersion []string -func (vs ByVersion) Len() int { return len(vs) } -func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } -func (vs ByVersion) Less(i, j int) bool { - cmp := Compare(vs[i], vs[j]) - if cmp != 0 { - return cmp < 0 - } - return vs[i] < vs[j] -} +func (vs ByVersion) Len() int { return len(vs) } +func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } +func (vs ByVersion) Less(i, j int) bool { return compareVersion(vs[i], vs[j]) < 0 } -// Sort sorts a list of semantic version strings using [ByVersion]. +// Sort sorts a list of semantic version strings using [Compare] and falls back +// to use [strings.Compare] if both versions are considered equal. func Sort(list []string) { - sort.Sort(ByVersion(list)) + slices.SortFunc(list, compareVersion) +} + +func compareVersion(a, b string) int { + cmp := Compare(a, b) + if cmp != 0 { + return cmp + } + return strings.Compare(a, b) } func parse(v string) (p parsed, ok bool) { diff --git a/vendor/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go index cf66309c4..24cea6882 100644 --- a/vendor/golang.org/x/net/context/context.go +++ b/vendor/golang.org/x/net/context/context.go @@ -2,55 +2,117 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package context defines the Context type, which carries deadlines, -// cancelation signals, and other request-scoped values across API boundaries -// and between processes. -// As of Go 1.7 this package is available in the standard library under the -// name context. https://golang.org/pkg/context. -// -// Incoming requests to a server should create a Context, and outgoing calls to -// servers should accept a Context. The chain of function calls between must -// propagate the Context, optionally replacing it with a modified copy created -// using WithDeadline, WithTimeout, WithCancel, or WithValue. -// -// Programs that use Contexts should follow these rules to keep interfaces -// consistent across packages and enable static analysis tools to check context -// propagation: -// -// Do not store Contexts inside a struct type; instead, pass a Context -// explicitly to each function that needs it. The Context should be the first -// parameter, typically named ctx: -// -// func DoSomething(ctx context.Context, arg Arg) error { -// // ... use ctx ... -// } +// Package context has been superseded by the standard library [context] package. // -// Do not pass a nil Context, even if a function permits it. Pass context.TODO -// if you are unsure about which Context to use. +// Deprecated: Use the standard library context package instead. +package context + +import ( + "context" // standard library's context, as of Go 1.7 + "time" +) + +// A Context carries a deadline, a cancellation signal, and other values across +// API boundaries. // -// Use context Values only for request-scoped data that transits processes and -// APIs, not for passing optional parameters to functions. +// Context's methods may be called by multiple goroutines simultaneously. +// +//go:fix inline +type Context = context.Context + +// Canceled is the error returned by [Context.Err] when the context is canceled +// for some reason other than its deadline passing. // -// The same Context may be passed to functions running in different goroutines; -// Contexts are safe for simultaneous use by multiple goroutines. +//go:fix inline +var Canceled = context.Canceled + +// DeadlineExceeded is the error returned by [Context.Err] when the context is canceled +// due to its deadline passing. // -// See http://blog.golang.org/context for example code for a server that uses -// Contexts. -package context // import "golang.org/x/net/context" +//go:fix inline +var DeadlineExceeded = context.DeadlineExceeded // Background returns a non-nil, empty Context. It is never canceled, has no // values, and has no deadline. It is typically used by the main function, // initialization, and tests, and as the top-level Context for incoming // requests. -func Background() Context { - return background -} +// +//go:fix inline +func Background() Context { return context.Background() } // TODO returns a non-nil, empty Context. Code should use context.TODO when // it's unclear which Context to use or it is not yet available (because the // surrounding function has not yet been extended to accept a Context -// parameter). TODO is recognized by static analysis tools that determine -// whether Contexts are propagated correctly in a program. -func TODO() Context { - return todo +// parameter). +// +//go:fix inline +func TODO() Context { return context.TODO() } + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// A CancelFunc may be called by multiple goroutines simultaneously. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc = context.CancelFunc + +// WithCancel returns a derived context that points to the parent context +// but has a new Done channel. The returned context's Done channel is closed +// when the returned cancel function is called or when the parent context's +// Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this [Context] complete. +// +//go:fix inline +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + return context.WithCancel(parent) +} + +// WithDeadline returns a derived context that points to the parent context +// but has the deadline adjusted to be no later than d. If the parent's +// deadline is already earlier than d, WithDeadline(parent, d) is semantically +// equivalent to parent. The returned [Context.Done] channel is closed when +// the deadline expires, when the returned cancel function is called, +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this [Context] complete. +// +//go:fix inline +func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { + return context.WithDeadline(parent, d) +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this [Context] complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +// +//go:fix inline +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return context.WithTimeout(parent, timeout) +} + +// WithValue returns a derived context that points to the parent Context. +// In the derived context, the value associated with key is val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The provided key must be comparable and should not be of type +// string or any other built-in type to avoid collisions between +// packages using context. Users of WithValue should define their own +// types for keys. To avoid allocating when assigning to an +// interface{}, context keys often have concrete type +// struct{}. Alternatively, exported context key variables' static +// type should be a pointer or interface. +// +//go:fix inline +func WithValue(parent Context, key, val interface{}) Context { + return context.WithValue(parent, key, val) } diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go deleted file mode 100644 index 0c1b86793..000000000 --- a/vendor/golang.org/x/net/context/go17.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.7 - -package context - -import ( - "context" // standard library's context, as of Go 1.7 - "time" -) - -var ( - todo = context.TODO() - background = context.Background() -) - -// Canceled is the error returned by Context.Err when the context is canceled. -var Canceled = context.Canceled - -// DeadlineExceeded is the error returned by Context.Err when the context's -// deadline passes. -var DeadlineExceeded = context.DeadlineExceeded - -// WithCancel returns a copy of parent with a new Done channel. The returned -// context's Done channel is closed when the returned cancel function is called -// or when the parent context's Done channel is closed, whichever happens first. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { - ctx, f := context.WithCancel(parent) - return ctx, f -} - -// WithDeadline returns a copy of the parent context with the deadline adjusted -// to be no later than d. If the parent's deadline is already earlier than d, -// WithDeadline(parent, d) is semantically equivalent to parent. The returned -// context's Done channel is closed when the deadline expires, when the returned -// cancel function is called, or when the parent context's Done channel is -// closed, whichever happens first. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { - ctx, f := context.WithDeadline(parent, deadline) - return ctx, f -} - -// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete: -// -// func slowOperationWithTimeout(ctx context.Context) (Result, error) { -// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) -// defer cancel() // releases resources if slowOperation completes before timeout elapses -// return slowOperation(ctx) -// } -func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { - return WithDeadline(parent, time.Now().Add(timeout)) -} - -// WithValue returns a copy of parent in which the value associated with key is -// val. -// -// Use context Values only for request-scoped data that transits processes and -// APIs, not for passing optional parameters to functions. -func WithValue(parent Context, key interface{}, val interface{}) Context { - return context.WithValue(parent, key, val) -} diff --git a/vendor/golang.org/x/net/context/go19.go b/vendor/golang.org/x/net/context/go19.go deleted file mode 100644 index e31e35a90..000000000 --- a/vendor/golang.org/x/net/context/go19.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.9 - -package context - -import "context" // standard library's context, as of Go 1.7 - -// A Context carries a deadline, a cancelation signal, and other values across -// API boundaries. -// -// Context's methods may be called by multiple goroutines simultaneously. -type Context = context.Context - -// A CancelFunc tells an operation to abandon its work. -// A CancelFunc does not wait for the work to stop. -// After the first call, subsequent calls to a CancelFunc do nothing. -type CancelFunc = context.CancelFunc diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go deleted file mode 100644 index 065ff3dfa..000000000 --- a/vendor/golang.org/x/net/context/pre_go17.go +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.7 - -package context - -import ( - "errors" - "fmt" - "sync" - "time" -) - -// An emptyCtx is never canceled, has no values, and has no deadline. It is not -// struct{}, since vars of this type must have distinct addresses. -type emptyCtx int - -func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { - return -} - -func (*emptyCtx) Done() <-chan struct{} { - return nil -} - -func (*emptyCtx) Err() error { - return nil -} - -func (*emptyCtx) Value(key interface{}) interface{} { - return nil -} - -func (e *emptyCtx) String() string { - switch e { - case background: - return "context.Background" - case todo: - return "context.TODO" - } - return "unknown empty Context" -} - -var ( - background = new(emptyCtx) - todo = new(emptyCtx) -) - -// Canceled is the error returned by Context.Err when the context is canceled. -var Canceled = errors.New("context canceled") - -// DeadlineExceeded is the error returned by Context.Err when the context's -// deadline passes. -var DeadlineExceeded = errors.New("context deadline exceeded") - -// WithCancel returns a copy of parent with a new Done channel. The returned -// context's Done channel is closed when the returned cancel function is called -// or when the parent context's Done channel is closed, whichever happens first. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { - c := newCancelCtx(parent) - propagateCancel(parent, c) - return c, func() { c.cancel(true, Canceled) } -} - -// newCancelCtx returns an initialized cancelCtx. -func newCancelCtx(parent Context) *cancelCtx { - return &cancelCtx{ - Context: parent, - done: make(chan struct{}), - } -} - -// propagateCancel arranges for child to be canceled when parent is. -func propagateCancel(parent Context, child canceler) { - if parent.Done() == nil { - return // parent is never canceled - } - if p, ok := parentCancelCtx(parent); ok { - p.mu.Lock() - if p.err != nil { - // parent has already been canceled - child.cancel(false, p.err) - } else { - if p.children == nil { - p.children = make(map[canceler]bool) - } - p.children[child] = true - } - p.mu.Unlock() - } else { - go func() { - select { - case <-parent.Done(): - child.cancel(false, parent.Err()) - case <-child.Done(): - } - }() - } -} - -// parentCancelCtx follows a chain of parent references until it finds a -// *cancelCtx. This function understands how each of the concrete types in this -// package represents its parent. -func parentCancelCtx(parent Context) (*cancelCtx, bool) { - for { - switch c := parent.(type) { - case *cancelCtx: - return c, true - case *timerCtx: - return c.cancelCtx, true - case *valueCtx: - parent = c.Context - default: - return nil, false - } - } -} - -// removeChild removes a context from its parent. -func removeChild(parent Context, child canceler) { - p, ok := parentCancelCtx(parent) - if !ok { - return - } - p.mu.Lock() - if p.children != nil { - delete(p.children, child) - } - p.mu.Unlock() -} - -// A canceler is a context type that can be canceled directly. The -// implementations are *cancelCtx and *timerCtx. -type canceler interface { - cancel(removeFromParent bool, err error) - Done() <-chan struct{} -} - -// A cancelCtx can be canceled. When canceled, it also cancels any children -// that implement canceler. -type cancelCtx struct { - Context - - done chan struct{} // closed by the first cancel call. - - mu sync.Mutex - children map[canceler]bool // set to nil by the first cancel call - err error // set to non-nil by the first cancel call -} - -func (c *cancelCtx) Done() <-chan struct{} { - return c.done -} - -func (c *cancelCtx) Err() error { - c.mu.Lock() - defer c.mu.Unlock() - return c.err -} - -func (c *cancelCtx) String() string { - return fmt.Sprintf("%v.WithCancel", c.Context) -} - -// cancel closes c.done, cancels each of c's children, and, if -// removeFromParent is true, removes c from its parent's children. -func (c *cancelCtx) cancel(removeFromParent bool, err error) { - if err == nil { - panic("context: internal error: missing cancel error") - } - c.mu.Lock() - if c.err != nil { - c.mu.Unlock() - return // already canceled - } - c.err = err - close(c.done) - for child := range c.children { - // NOTE: acquiring the child's lock while holding parent's lock. - child.cancel(false, err) - } - c.children = nil - c.mu.Unlock() - - if removeFromParent { - removeChild(c.Context, c) - } -} - -// WithDeadline returns a copy of the parent context with the deadline adjusted -// to be no later than d. If the parent's deadline is already earlier than d, -// WithDeadline(parent, d) is semantically equivalent to parent. The returned -// context's Done channel is closed when the deadline expires, when the returned -// cancel function is called, or when the parent context's Done channel is -// closed, whichever happens first. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { - if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { - // The current deadline is already sooner than the new one. - return WithCancel(parent) - } - c := &timerCtx{ - cancelCtx: newCancelCtx(parent), - deadline: deadline, - } - propagateCancel(parent, c) - d := deadline.Sub(time.Now()) - if d <= 0 { - c.cancel(true, DeadlineExceeded) // deadline has already passed - return c, func() { c.cancel(true, Canceled) } - } - c.mu.Lock() - defer c.mu.Unlock() - if c.err == nil { - c.timer = time.AfterFunc(d, func() { - c.cancel(true, DeadlineExceeded) - }) - } - return c, func() { c.cancel(true, Canceled) } -} - -// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to -// implement Done and Err. It implements cancel by stopping its timer then -// delegating to cancelCtx.cancel. -type timerCtx struct { - *cancelCtx - timer *time.Timer // Under cancelCtx.mu. - - deadline time.Time -} - -func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { - return c.deadline, true -} - -func (c *timerCtx) String() string { - return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) -} - -func (c *timerCtx) cancel(removeFromParent bool, err error) { - c.cancelCtx.cancel(false, err) - if removeFromParent { - // Remove this timerCtx from its parent cancelCtx's children. - removeChild(c.cancelCtx.Context, c) - } - c.mu.Lock() - if c.timer != nil { - c.timer.Stop() - c.timer = nil - } - c.mu.Unlock() -} - -// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete: -// -// func slowOperationWithTimeout(ctx context.Context) (Result, error) { -// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) -// defer cancel() // releases resources if slowOperation completes before timeout elapses -// return slowOperation(ctx) -// } -func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { - return WithDeadline(parent, time.Now().Add(timeout)) -} - -// WithValue returns a copy of parent in which the value associated with key is -// val. -// -// Use context Values only for request-scoped data that transits processes and -// APIs, not for passing optional parameters to functions. -func WithValue(parent Context, key interface{}, val interface{}) Context { - return &valueCtx{parent, key, val} -} - -// A valueCtx carries a key-value pair. It implements Value for that key and -// delegates all other calls to the embedded Context. -type valueCtx struct { - Context - key, val interface{} -} - -func (c *valueCtx) String() string { - return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) -} - -func (c *valueCtx) Value(key interface{}) interface{} { - if c.key == key { - return c.val - } - return c.Context.Value(key) -} diff --git a/vendor/golang.org/x/net/context/pre_go19.go b/vendor/golang.org/x/net/context/pre_go19.go deleted file mode 100644 index ec5a63803..000000000 --- a/vendor/golang.org/x/net/context/pre_go19.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.9 - -package context - -import "time" - -// A Context carries a deadline, a cancelation signal, and other values across -// API boundaries. -// -// Context's methods may be called by multiple goroutines simultaneously. -type Context interface { - // Deadline returns the time when work done on behalf of this context - // should be canceled. Deadline returns ok==false when no deadline is - // set. Successive calls to Deadline return the same results. - Deadline() (deadline time.Time, ok bool) - - // Done returns a channel that's closed when work done on behalf of this - // context should be canceled. Done may return nil if this context can - // never be canceled. Successive calls to Done return the same value. - // - // WithCancel arranges for Done to be closed when cancel is called; - // WithDeadline arranges for Done to be closed when the deadline - // expires; WithTimeout arranges for Done to be closed when the timeout - // elapses. - // - // Done is provided for use in select statements: - // - // // Stream generates values with DoSomething and sends them to out - // // until DoSomething returns an error or ctx.Done is closed. - // func Stream(ctx context.Context, out chan<- Value) error { - // for { - // v, err := DoSomething(ctx) - // if err != nil { - // return err - // } - // select { - // case <-ctx.Done(): - // return ctx.Err() - // case out <- v: - // } - // } - // } - // - // See http://blog.golang.org/pipelines for more examples of how to use - // a Done channel for cancelation. - Done() <-chan struct{} - - // Err returns a non-nil error value after Done is closed. Err returns - // Canceled if the context was canceled or DeadlineExceeded if the - // context's deadline passed. No other values for Err are defined. - // After Done is closed, successive calls to Err return the same value. - Err() error - - // Value returns the value associated with this context for key, or nil - // if no value is associated with key. Successive calls to Value with - // the same key returns the same result. - // - // Use context values only for request-scoped data that transits - // processes and API boundaries, not for passing optional parameters to - // functions. - // - // A key identifies a specific value in a Context. Functions that wish - // to store values in Context typically allocate a key in a global - // variable then use that key as the argument to context.WithValue and - // Context.Value. A key can be any type that supports equality; - // packages should define keys as an unexported type to avoid - // collisions. - // - // Packages that define a Context key should provide type-safe accessors - // for the values stores using that key: - // - // // Package user defines a User type that's stored in Contexts. - // package user - // - // import "golang.org/x/net/context" - // - // // User is the type of value stored in the Contexts. - // type User struct {...} - // - // // key is an unexported type for keys defined in this package. - // // This prevents collisions with keys defined in other packages. - // type key int - // - // // userKey is the key for user.User values in Contexts. It is - // // unexported; clients use user.NewContext and user.FromContext - // // instead of using this key directly. - // var userKey key = 0 - // - // // NewContext returns a new Context that carries value u. - // func NewContext(ctx context.Context, u *User) context.Context { - // return context.WithValue(ctx, userKey, u) - // } - // - // // FromContext returns the User value stored in ctx, if any. - // func FromContext(ctx context.Context) (*User, bool) { - // u, ok := ctx.Value(userKey).(*User) - // return u, ok - // } - Value(key interface{}) interface{} -} - -// A CancelFunc tells an operation to abandon its work. -// A CancelFunc does not wait for the work to stop. -// After the first call, subsequent calls to a CancelFunc do nothing. -type CancelFunc func() diff --git a/vendor/golang.org/x/net/html/atom/table.go b/vendor/golang.org/x/net/html/atom/table.go index 2a938864c..b460e6f72 100644 --- a/vendor/golang.org/x/net/html/atom/table.go +++ b/vendor/golang.org/x/net/html/atom/table.go @@ -11,23 +11,23 @@ const ( AcceptCharset Atom = 0x1a0e Accesskey Atom = 0x2c09 Acronym Atom = 0xaa07 - Action Atom = 0x27206 - Address Atom = 0x6f307 + Action Atom = 0x26506 + Address Atom = 0x6f107 Align Atom = 0xb105 - Allowfullscreen Atom = 0x2080f + Allowfullscreen Atom = 0x3280f Allowpaymentrequest Atom = 0xc113 Allowusermedia Atom = 0xdd0e Alt Atom = 0xf303 Annotation Atom = 0x1c90a AnnotationXml Atom = 0x1c90e - Applet Atom = 0x31906 - Area Atom = 0x35604 - Article Atom = 0x3fc07 + Applet Atom = 0x30806 + Area Atom = 0x35004 + Article Atom = 0x3f607 As Atom = 0x3c02 Aside Atom = 0x10705 Async Atom = 0xff05 Audio Atom = 0x11505 - Autocomplete Atom = 0x2780c + Autocomplete Atom = 0x26b0c Autofocus Atom = 0x12109 Autoplay Atom = 0x13c08 B Atom = 0x101 @@ -43,34 +43,34 @@ const ( Br Atom = 0x202 Button Atom = 0x19106 Canvas Atom = 0x10306 - Caption Atom = 0x23107 - Center Atom = 0x22006 - Challenge Atom = 0x29b09 + Caption Atom = 0x22407 + Center Atom = 0x21306 + Challenge Atom = 0x28e09 Charset Atom = 0x2107 - Checked Atom = 0x47907 + Checked Atom = 0x5b507 Cite Atom = 0x19c04 - Class Atom = 0x56405 - Code Atom = 0x5c504 + Class Atom = 0x55805 + Code Atom = 0x5ee04 Col Atom = 0x1ab03 Colgroup Atom = 0x1ab08 Color Atom = 0x1bf05 Cols Atom = 0x1c404 Colspan Atom = 0x1c407 Command Atom = 0x1d707 - Content Atom = 0x58b07 - Contenteditable Atom = 0x58b0f - Contextmenu Atom = 0x3800b + Content Atom = 0x57b07 + Contenteditable Atom = 0x57b0f + Contextmenu Atom = 0x37a0b Controls Atom = 0x1de08 - Coords Atom = 0x1ea06 - Crossorigin Atom = 0x1fb0b - Data Atom = 0x4a504 - Datalist Atom = 0x4a508 - Datetime Atom = 0x2b808 - Dd Atom = 0x2d702 + Coords Atom = 0x1f006 + Crossorigin Atom = 0x1fa0b + Data Atom = 0x49904 + Datalist Atom = 0x49908 + Datetime Atom = 0x2ab08 + Dd Atom = 0x2bf02 Default Atom = 0x10a07 - Defer Atom = 0x5c705 - Del Atom = 0x45203 - Desc Atom = 0x56104 + Defer Atom = 0x5f005 + Del Atom = 0x44c03 + Desc Atom = 0x55504 Details Atom = 0x7207 Dfn Atom = 0x8703 Dialog Atom = 0xbb06 @@ -78,106 +78,106 @@ const ( Dirname Atom = 0x9307 Disabled Atom = 0x16408 Div Atom = 0x16b03 - Dl Atom = 0x5e602 - Download Atom = 0x46308 + Dl Atom = 0x5d602 + Download Atom = 0x45d08 Draggable Atom = 0x17a09 - Dropzone Atom = 0x40508 - Dt Atom = 0x64b02 + Dropzone Atom = 0x3ff08 + Dt Atom = 0x64002 Em Atom = 0x6e02 Embed Atom = 0x6e05 - Enctype Atom = 0x28d07 - Face Atom = 0x21e04 - Fieldset Atom = 0x22608 - Figcaption Atom = 0x22e0a - Figure Atom = 0x24806 + Enctype Atom = 0x28007 + Face Atom = 0x21104 + Fieldset Atom = 0x21908 + Figcaption Atom = 0x2210a + Figure Atom = 0x23b06 Font Atom = 0x3f04 Footer Atom = 0xf606 - For Atom = 0x25403 - ForeignObject Atom = 0x2540d - Foreignobject Atom = 0x2610d - Form Atom = 0x26e04 - Formaction Atom = 0x26e0a - Formenctype Atom = 0x2890b - Formmethod Atom = 0x2a40a - Formnovalidate Atom = 0x2ae0e - Formtarget Atom = 0x2c00a + For Atom = 0x24703 + ForeignObject Atom = 0x2470d + Foreignobject Atom = 0x2540d + Form Atom = 0x26104 + Formaction Atom = 0x2610a + Formenctype Atom = 0x27c0b + Formmethod Atom = 0x2970a + Formnovalidate Atom = 0x2a10e + Formtarget Atom = 0x2b30a Frame Atom = 0x8b05 Frameset Atom = 0x8b08 H1 Atom = 0x15c02 - H2 Atom = 0x2de02 - H3 Atom = 0x30d02 - H4 Atom = 0x34502 - H5 Atom = 0x34f02 - H6 Atom = 0x64d02 - Head Atom = 0x33104 - Header Atom = 0x33106 - Headers Atom = 0x33107 + H2 Atom = 0x56102 + H3 Atom = 0x2cd02 + H4 Atom = 0x2fc02 + H5 Atom = 0x33f02 + H6 Atom = 0x34902 + Head Atom = 0x32004 + Header Atom = 0x32006 + Headers Atom = 0x32007 Height Atom = 0x5206 - Hgroup Atom = 0x2ca06 - Hidden Atom = 0x2d506 - High Atom = 0x2db04 + Hgroup Atom = 0x64206 + Hidden Atom = 0x2bd06 + High Atom = 0x2ca04 Hr Atom = 0x15702 - Href Atom = 0x2e004 - Hreflang Atom = 0x2e008 + Href Atom = 0x2cf04 + Hreflang Atom = 0x2cf08 Html Atom = 0x5604 - HttpEquiv Atom = 0x2e80a + HttpEquiv Atom = 0x2d70a I Atom = 0x601 - Icon Atom = 0x58a04 + Icon Atom = 0x57a04 Id Atom = 0x10902 - Iframe Atom = 0x2fc06 - Image Atom = 0x30205 - Img Atom = 0x30703 - Input Atom = 0x44b05 - Inputmode Atom = 0x44b09 - Ins Atom = 0x20403 - Integrity Atom = 0x23f09 + Iframe Atom = 0x2eb06 + Image Atom = 0x2f105 + Img Atom = 0x2f603 + Input Atom = 0x44505 + Inputmode Atom = 0x44509 + Ins Atom = 0x20303 + Integrity Atom = 0x23209 Is Atom = 0x16502 - Isindex Atom = 0x30f07 - Ismap Atom = 0x31605 - Itemid Atom = 0x38b06 + Isindex Atom = 0x2fe07 + Ismap Atom = 0x30505 + Itemid Atom = 0x38506 Itemprop Atom = 0x19d08 - Itemref Atom = 0x3cd07 - Itemscope Atom = 0x67109 - Itemtype Atom = 0x31f08 + Itemref Atom = 0x3c707 + Itemscope Atom = 0x66f09 + Itemtype Atom = 0x30e08 Kbd Atom = 0xb903 Keygen Atom = 0x3206 Keytype Atom = 0xd607 Kind Atom = 0x17704 Label Atom = 0x5905 - Lang Atom = 0x2e404 + Lang Atom = 0x2d304 Legend Atom = 0x18106 Li Atom = 0xb202 Link Atom = 0x17404 - List Atom = 0x4a904 - Listing Atom = 0x4a907 + List Atom = 0x49d04 + Listing Atom = 0x49d07 Loop Atom = 0x5d04 Low Atom = 0xc303 Main Atom = 0x1004 Malignmark Atom = 0xb00a - Manifest Atom = 0x6d708 - Map Atom = 0x31803 + Manifest Atom = 0x6d508 + Map Atom = 0x30703 Mark Atom = 0xb604 - Marquee Atom = 0x32707 - Math Atom = 0x32e04 - Max Atom = 0x33d03 - Maxlength Atom = 0x33d09 + Marquee Atom = 0x31607 + Math Atom = 0x31d04 + Max Atom = 0x33703 + Maxlength Atom = 0x33709 Media Atom = 0xe605 Mediagroup Atom = 0xe60a - Menu Atom = 0x38704 - Menuitem Atom = 0x38708 - Meta Atom = 0x4b804 + Menu Atom = 0x38104 + Menuitem Atom = 0x38108 + Meta Atom = 0x4ac04 Meter Atom = 0x9805 - Method Atom = 0x2a806 - Mglyph Atom = 0x30806 - Mi Atom = 0x34702 - Min Atom = 0x34703 - Minlength Atom = 0x34709 - Mn Atom = 0x2b102 + Method Atom = 0x29b06 + Mglyph Atom = 0x2f706 + Mi Atom = 0x34102 + Min Atom = 0x34103 + Minlength Atom = 0x34109 + Mn Atom = 0x2a402 Mo Atom = 0xa402 - Ms Atom = 0x67402 - Mtext Atom = 0x35105 - Multiple Atom = 0x35f08 - Muted Atom = 0x36705 + Ms Atom = 0x67202 + Mtext Atom = 0x34b05 + Multiple Atom = 0x35908 + Muted Atom = 0x36105 Name Atom = 0x9604 Nav Atom = 0x1303 Nobr Atom = 0x3704 @@ -185,101 +185,101 @@ const ( Noframes Atom = 0x8908 Nomodule Atom = 0xa208 Nonce Atom = 0x1a605 - Noscript Atom = 0x21608 - Novalidate Atom = 0x2b20a - Object Atom = 0x26806 + Noscript Atom = 0x2c208 + Novalidate Atom = 0x2a50a + Object Atom = 0x25b06 Ol Atom = 0x13702 Onabort Atom = 0x19507 - Onafterprint Atom = 0x2360c - Onautocomplete Atom = 0x2760e - Onautocompleteerror Atom = 0x27613 - Onauxclick Atom = 0x61f0a - Onbeforeprint Atom = 0x69e0d - Onbeforeunload Atom = 0x6e70e - Onblur Atom = 0x56d06 + Onafterprint Atom = 0x2290c + Onautocomplete Atom = 0x2690e + Onautocompleteerror Atom = 0x26913 + Onauxclick Atom = 0x6140a + Onbeforeprint Atom = 0x69c0d + Onbeforeunload Atom = 0x6e50e + Onblur Atom = 0x1ea06 Oncancel Atom = 0x11908 Oncanplay Atom = 0x14d09 Oncanplaythrough Atom = 0x14d10 - Onchange Atom = 0x41b08 - Onclick Atom = 0x2f507 - Onclose Atom = 0x36c07 - Oncontextmenu Atom = 0x37e0d - Oncopy Atom = 0x39106 - Oncuechange Atom = 0x3970b - Oncut Atom = 0x3a205 - Ondblclick Atom = 0x3a70a - Ondrag Atom = 0x3b106 - Ondragend Atom = 0x3b109 - Ondragenter Atom = 0x3ba0b - Ondragexit Atom = 0x3c50a - Ondragleave Atom = 0x3df0b - Ondragover Atom = 0x3ea0a - Ondragstart Atom = 0x3f40b - Ondrop Atom = 0x40306 - Ondurationchange Atom = 0x41310 - Onemptied Atom = 0x40a09 - Onended Atom = 0x42307 - Onerror Atom = 0x42a07 - Onfocus Atom = 0x43107 - Onhashchange Atom = 0x43d0c - Oninput Atom = 0x44907 - Oninvalid Atom = 0x45509 - Onkeydown Atom = 0x45e09 - Onkeypress Atom = 0x46b0a - Onkeyup Atom = 0x48007 - Onlanguagechange Atom = 0x48d10 - Onload Atom = 0x49d06 - Onloadeddata Atom = 0x49d0c - Onloadedmetadata Atom = 0x4b010 - Onloadend Atom = 0x4c609 - Onloadstart Atom = 0x4cf0b - Onmessage Atom = 0x4da09 - Onmessageerror Atom = 0x4da0e - Onmousedown Atom = 0x4e80b - Onmouseenter Atom = 0x4f30c - Onmouseleave Atom = 0x4ff0c - Onmousemove Atom = 0x50b0b - Onmouseout Atom = 0x5160a - Onmouseover Atom = 0x5230b - Onmouseup Atom = 0x52e09 - Onmousewheel Atom = 0x53c0c - Onoffline Atom = 0x54809 - Ononline Atom = 0x55108 - Onpagehide Atom = 0x5590a - Onpageshow Atom = 0x5730a - Onpaste Atom = 0x57f07 - Onpause Atom = 0x59a07 - Onplay Atom = 0x5a406 - Onplaying Atom = 0x5a409 - Onpopstate Atom = 0x5ad0a - Onprogress Atom = 0x5b70a - Onratechange Atom = 0x5cc0c - Onrejectionhandled Atom = 0x5d812 - Onreset Atom = 0x5ea07 - Onresize Atom = 0x5f108 - Onscroll Atom = 0x60008 - Onsecuritypolicyviolation Atom = 0x60819 - Onseeked Atom = 0x62908 - Onseeking Atom = 0x63109 - Onselect Atom = 0x63a08 - Onshow Atom = 0x64406 - Onsort Atom = 0x64f06 - Onstalled Atom = 0x65909 - Onstorage Atom = 0x66209 - Onsubmit Atom = 0x66b08 - Onsuspend Atom = 0x67b09 + Onchange Atom = 0x41508 + Onclick Atom = 0x2e407 + Onclose Atom = 0x36607 + Oncontextmenu Atom = 0x3780d + Oncopy Atom = 0x38b06 + Oncuechange Atom = 0x3910b + Oncut Atom = 0x39c05 + Ondblclick Atom = 0x3a10a + Ondrag Atom = 0x3ab06 + Ondragend Atom = 0x3ab09 + Ondragenter Atom = 0x3b40b + Ondragexit Atom = 0x3bf0a + Ondragleave Atom = 0x3d90b + Ondragover Atom = 0x3e40a + Ondragstart Atom = 0x3ee0b + Ondrop Atom = 0x3fd06 + Ondurationchange Atom = 0x40d10 + Onemptied Atom = 0x40409 + Onended Atom = 0x41d07 + Onerror Atom = 0x42407 + Onfocus Atom = 0x42b07 + Onhashchange Atom = 0x4370c + Oninput Atom = 0x44307 + Oninvalid Atom = 0x44f09 + Onkeydown Atom = 0x45809 + Onkeypress Atom = 0x4650a + Onkeyup Atom = 0x47407 + Onlanguagechange Atom = 0x48110 + Onload Atom = 0x49106 + Onloadeddata Atom = 0x4910c + Onloadedmetadata Atom = 0x4a410 + Onloadend Atom = 0x4ba09 + Onloadstart Atom = 0x4c30b + Onmessage Atom = 0x4ce09 + Onmessageerror Atom = 0x4ce0e + Onmousedown Atom = 0x4dc0b + Onmouseenter Atom = 0x4e70c + Onmouseleave Atom = 0x4f30c + Onmousemove Atom = 0x4ff0b + Onmouseout Atom = 0x50a0a + Onmouseover Atom = 0x5170b + Onmouseup Atom = 0x52209 + Onmousewheel Atom = 0x5300c + Onoffline Atom = 0x53c09 + Ononline Atom = 0x54508 + Onpagehide Atom = 0x54d0a + Onpageshow Atom = 0x5630a + Onpaste Atom = 0x56f07 + Onpause Atom = 0x58a07 + Onplay Atom = 0x59406 + Onplaying Atom = 0x59409 + Onpopstate Atom = 0x59d0a + Onprogress Atom = 0x5a70a + Onratechange Atom = 0x5bc0c + Onrejectionhandled Atom = 0x5c812 + Onreset Atom = 0x5da07 + Onresize Atom = 0x5e108 + Onscroll Atom = 0x5f508 + Onsecuritypolicyviolation Atom = 0x5fd19 + Onseeked Atom = 0x61e08 + Onseeking Atom = 0x62609 + Onselect Atom = 0x62f08 + Onshow Atom = 0x63906 + Onsort Atom = 0x64d06 + Onstalled Atom = 0x65709 + Onstorage Atom = 0x66009 + Onsubmit Atom = 0x66908 + Onsuspend Atom = 0x67909 Ontimeupdate Atom = 0x400c - Ontoggle Atom = 0x68408 - Onunhandledrejection Atom = 0x68c14 - Onunload Atom = 0x6ab08 - Onvolumechange Atom = 0x6b30e - Onwaiting Atom = 0x6c109 - Onwheel Atom = 0x6ca07 + Ontoggle Atom = 0x68208 + Onunhandledrejection Atom = 0x68a14 + Onunload Atom = 0x6a908 + Onvolumechange Atom = 0x6b10e + Onwaiting Atom = 0x6bf09 + Onwheel Atom = 0x6c807 Open Atom = 0x1a304 Optgroup Atom = 0x5f08 - Optimum Atom = 0x6d107 - Option Atom = 0x6e306 - Output Atom = 0x51d06 + Optimum Atom = 0x6cf07 + Option Atom = 0x6e106 + Output Atom = 0x51106 P Atom = 0xc01 Param Atom = 0xc05 Pattern Atom = 0x6607 @@ -288,466 +288,468 @@ const ( Placeholder Atom = 0x1310b Plaintext Atom = 0x1b209 Playsinline Atom = 0x1400b - Poster Atom = 0x2cf06 - Pre Atom = 0x47003 - Preload Atom = 0x48607 - Progress Atom = 0x5b908 - Prompt Atom = 0x53606 - Public Atom = 0x58606 + Poster Atom = 0x64706 + Pre Atom = 0x46a03 + Preload Atom = 0x47a07 + Progress Atom = 0x5a908 + Prompt Atom = 0x52a06 + Public Atom = 0x57606 Q Atom = 0xcf01 Radiogroup Atom = 0x30a Rb Atom = 0x3a02 - Readonly Atom = 0x35708 - Referrerpolicy Atom = 0x3d10e - Rel Atom = 0x48703 - Required Atom = 0x24c08 + Readonly Atom = 0x35108 + Referrerpolicy Atom = 0x3cb0e + Rel Atom = 0x47b03 + Required Atom = 0x23f08 Reversed Atom = 0x8008 Rows Atom = 0x9c04 Rowspan Atom = 0x9c07 - Rp Atom = 0x23c02 + Rp Atom = 0x22f02 Rt Atom = 0x19a02 Rtc Atom = 0x19a03 Ruby Atom = 0xfb04 S Atom = 0x2501 Samp Atom = 0x7804 Sandbox Atom = 0x12907 - Scope Atom = 0x67505 - Scoped Atom = 0x67506 - Script Atom = 0x21806 - Seamless Atom = 0x37108 - Section Atom = 0x56807 - Select Atom = 0x63c06 - Selected Atom = 0x63c08 - Shape Atom = 0x1e505 - Size Atom = 0x5f504 - Sizes Atom = 0x5f505 - Slot Atom = 0x1ef04 - Small Atom = 0x20605 - Sortable Atom = 0x65108 - Sorted Atom = 0x33706 - Source Atom = 0x37806 - Spacer Atom = 0x43706 + Scope Atom = 0x67305 + Scoped Atom = 0x67306 + Script Atom = 0x2c406 + Seamless Atom = 0x36b08 + Search Atom = 0x55c06 + Section Atom = 0x1e507 + Select Atom = 0x63106 + Selected Atom = 0x63108 + Shape Atom = 0x1f505 + Size Atom = 0x5e504 + Sizes Atom = 0x5e505 + Slot Atom = 0x20504 + Small Atom = 0x32605 + Sortable Atom = 0x64f08 + Sorted Atom = 0x37206 + Source Atom = 0x43106 + Spacer Atom = 0x46e06 Span Atom = 0x9f04 - Spellcheck Atom = 0x4740a - Src Atom = 0x5c003 - Srcdoc Atom = 0x5c006 - Srclang Atom = 0x5f907 - Srcset Atom = 0x6f906 - Start Atom = 0x3fa05 - Step Atom = 0x58304 + Spellcheck Atom = 0x5b00a + Src Atom = 0x5e903 + Srcdoc Atom = 0x5e906 + Srclang Atom = 0x6f707 + Srcset Atom = 0x6fe06 + Start Atom = 0x3f405 + Step Atom = 0x57304 Strike Atom = 0xd206 - Strong Atom = 0x6dd06 - Style Atom = 0x6ff05 - Sub Atom = 0x66d03 - Summary Atom = 0x70407 - Sup Atom = 0x70b03 - Svg Atom = 0x70e03 - System Atom = 0x71106 - Tabindex Atom = 0x4be08 - Table Atom = 0x59505 - Target Atom = 0x2c406 + Strong Atom = 0x6db06 + Style Atom = 0x70405 + Sub Atom = 0x66b03 + Summary Atom = 0x70907 + Sup Atom = 0x71003 + Svg Atom = 0x71303 + System Atom = 0x71606 + Tabindex Atom = 0x4b208 + Table Atom = 0x58505 + Target Atom = 0x2b706 Tbody Atom = 0x2705 Td Atom = 0x9202 - Template Atom = 0x71408 - Textarea Atom = 0x35208 + Template Atom = 0x71908 + Textarea Atom = 0x34c08 Tfoot Atom = 0xf505 Th Atom = 0x15602 - Thead Atom = 0x33005 + Thead Atom = 0x31f05 Time Atom = 0x4204 Title Atom = 0x11005 Tr Atom = 0xcc02 Track Atom = 0x1ba05 - Translate Atom = 0x1f209 + Translate Atom = 0x20809 Tt Atom = 0x6802 Type Atom = 0xd904 - Typemustmatch Atom = 0x2900d + Typemustmatch Atom = 0x2830d U Atom = 0xb01 Ul Atom = 0xa702 Updateviacache Atom = 0x460e - Usemap Atom = 0x59e06 + Usemap Atom = 0x58e06 Value Atom = 0x1505 Var Atom = 0x16d03 - Video Atom = 0x2f105 - Wbr Atom = 0x57c03 - Width Atom = 0x64905 - Workertype Atom = 0x71c0a - Wrap Atom = 0x72604 + Video Atom = 0x2e005 + Wbr Atom = 0x56c03 + Width Atom = 0x63e05 + Workertype Atom = 0x7210a + Wrap Atom = 0x72b04 Xmp Atom = 0x12f03 ) -const hash0 = 0x81cdf10e +const hash0 = 0x84f70e16 const maxAtomLen = 25 var table = [1 << 9]Atom{ - 0x1: 0xe60a, // mediagroup - 0x2: 0x2e404, // lang - 0x4: 0x2c09, // accesskey - 0x5: 0x8b08, // frameset - 0x7: 0x63a08, // onselect - 0x8: 0x71106, // system - 0xa: 0x64905, // width - 0xc: 0x2890b, // formenctype - 0xd: 0x13702, // ol - 0xe: 0x3970b, // oncuechange - 0x10: 0x14b03, // bdo - 0x11: 0x11505, // audio - 0x12: 0x17a09, // draggable - 0x14: 0x2f105, // video - 0x15: 0x2b102, // mn - 0x16: 0x38704, // menu - 0x17: 0x2cf06, // poster - 0x19: 0xf606, // footer - 0x1a: 0x2a806, // method - 0x1b: 0x2b808, // datetime - 0x1c: 0x19507, // onabort - 0x1d: 0x460e, // updateviacache - 0x1e: 0xff05, // async - 0x1f: 0x49d06, // onload - 0x21: 0x11908, // oncancel - 0x22: 0x62908, // onseeked - 0x23: 0x30205, // image - 0x24: 0x5d812, // onrejectionhandled - 0x26: 0x17404, // link - 0x27: 0x51d06, // output - 0x28: 0x33104, // head - 0x29: 0x4ff0c, // onmouseleave - 0x2a: 0x57f07, // onpaste - 0x2b: 0x5a409, // onplaying - 0x2c: 0x1c407, // colspan - 0x2f: 0x1bf05, // color - 0x30: 0x5f504, // size - 0x31: 0x2e80a, // http-equiv - 0x33: 0x601, // i - 0x34: 0x5590a, // onpagehide - 0x35: 0x68c14, // onunhandledrejection - 0x37: 0x42a07, // onerror - 0x3a: 0x3b08, // basefont - 0x3f: 0x1303, // nav - 0x40: 0x17704, // kind - 0x41: 0x35708, // readonly - 0x42: 0x30806, // mglyph - 0x44: 0xb202, // li - 0x46: 0x2d506, // hidden - 0x47: 0x70e03, // svg - 0x48: 0x58304, // step - 0x49: 0x23f09, // integrity - 0x4a: 0x58606, // public - 0x4c: 0x1ab03, // col - 0x4d: 0x1870a, // blockquote - 0x4e: 0x34f02, // h5 - 0x50: 0x5b908, // progress - 0x51: 0x5f505, // sizes - 0x52: 0x34502, // h4 - 0x56: 0x33005, // thead - 0x57: 0xd607, // keytype - 0x58: 0x5b70a, // onprogress - 0x59: 0x44b09, // inputmode - 0x5a: 0x3b109, // ondragend - 0x5d: 0x3a205, // oncut - 0x5e: 0x43706, // spacer - 0x5f: 0x1ab08, // colgroup - 0x62: 0x16502, // is - 0x65: 0x3c02, // as - 0x66: 0x54809, // onoffline - 0x67: 0x33706, // sorted - 0x69: 0x48d10, // onlanguagechange - 0x6c: 0x43d0c, // onhashchange - 0x6d: 0x9604, // name - 0x6e: 0xf505, // tfoot - 0x6f: 0x56104, // desc - 0x70: 0x33d03, // max - 0x72: 0x1ea06, // coords - 0x73: 0x30d02, // h3 - 0x74: 0x6e70e, // onbeforeunload - 0x75: 0x9c04, // rows - 0x76: 0x63c06, // select - 0x77: 0x9805, // meter - 0x78: 0x38b06, // itemid - 0x79: 0x53c0c, // onmousewheel - 0x7a: 0x5c006, // srcdoc - 0x7d: 0x1ba05, // track - 0x7f: 0x31f08, // itemtype - 0x82: 0xa402, // mo - 0x83: 0x41b08, // onchange - 0x84: 0x33107, // headers - 0x85: 0x5cc0c, // onratechange - 0x86: 0x60819, // onsecuritypolicyviolation - 0x88: 0x4a508, // datalist - 0x89: 0x4e80b, // onmousedown - 0x8a: 0x1ef04, // slot - 0x8b: 0x4b010, // onloadedmetadata - 0x8c: 0x1a06, // accept - 0x8d: 0x26806, // object - 0x91: 0x6b30e, // onvolumechange - 0x92: 0x2107, // charset - 0x93: 0x27613, // onautocompleteerror - 0x94: 0xc113, // allowpaymentrequest - 0x95: 0x2804, // body - 0x96: 0x10a07, // default - 0x97: 0x63c08, // selected - 0x98: 0x21e04, // face - 0x99: 0x1e505, // shape - 0x9b: 0x68408, // ontoggle - 0x9e: 0x64b02, // dt - 0x9f: 0xb604, // mark - 0xa1: 0xb01, // u - 0xa4: 0x6ab08, // onunload - 0xa5: 0x5d04, // loop - 0xa6: 0x16408, // disabled - 0xaa: 0x42307, // onended - 0xab: 0xb00a, // malignmark - 0xad: 0x67b09, // onsuspend - 0xae: 0x35105, // mtext - 0xaf: 0x64f06, // onsort - 0xb0: 0x19d08, // itemprop - 0xb3: 0x67109, // itemscope - 0xb4: 0x17305, // blink - 0xb6: 0x3b106, // ondrag - 0xb7: 0xa702, // ul - 0xb8: 0x26e04, // form - 0xb9: 0x12907, // sandbox - 0xba: 0x8b05, // frame - 0xbb: 0x1505, // value - 0xbc: 0x66209, // onstorage - 0xbf: 0xaa07, // acronym - 0xc0: 0x19a02, // rt - 0xc2: 0x202, // br - 0xc3: 0x22608, // fieldset - 0xc4: 0x2900d, // typemustmatch - 0xc5: 0xa208, // nomodule - 0xc6: 0x6c07, // noembed - 0xc7: 0x69e0d, // onbeforeprint - 0xc8: 0x19106, // button - 0xc9: 0x2f507, // onclick - 0xca: 0x70407, // summary - 0xcd: 0xfb04, // ruby - 0xce: 0x56405, // class - 0xcf: 0x3f40b, // ondragstart - 0xd0: 0x23107, // caption - 0xd4: 0xdd0e, // allowusermedia - 0xd5: 0x4cf0b, // onloadstart - 0xd9: 0x16b03, // div - 0xda: 0x4a904, // list - 0xdb: 0x32e04, // math - 0xdc: 0x44b05, // input - 0xdf: 0x3ea0a, // ondragover - 0xe0: 0x2de02, // h2 - 0xe2: 0x1b209, // plaintext - 0xe4: 0x4f30c, // onmouseenter - 0xe7: 0x47907, // checked - 0xe8: 0x47003, // pre - 0xea: 0x35f08, // multiple - 0xeb: 0xba03, // bdi - 0xec: 0x33d09, // maxlength - 0xed: 0xcf01, // q - 0xee: 0x61f0a, // onauxclick - 0xf0: 0x57c03, // wbr - 0xf2: 0x3b04, // base - 0xf3: 0x6e306, // option - 0xf5: 0x41310, // ondurationchange - 0xf7: 0x8908, // noframes - 0xf9: 0x40508, // dropzone - 0xfb: 0x67505, // scope - 0xfc: 0x8008, // reversed - 0xfd: 0x3ba0b, // ondragenter - 0xfe: 0x3fa05, // start - 0xff: 0x12f03, // xmp - 0x100: 0x5f907, // srclang - 0x101: 0x30703, // img - 0x104: 0x101, // b - 0x105: 0x25403, // for - 0x106: 0x10705, // aside - 0x107: 0x44907, // oninput - 0x108: 0x35604, // area - 0x109: 0x2a40a, // formmethod - 0x10a: 0x72604, // wrap - 0x10c: 0x23c02, // rp - 0x10d: 0x46b0a, // onkeypress - 0x10e: 0x6802, // tt - 0x110: 0x34702, // mi - 0x111: 0x36705, // muted - 0x112: 0xf303, // alt - 0x113: 0x5c504, // code - 0x114: 0x6e02, // em - 0x115: 0x3c50a, // ondragexit - 0x117: 0x9f04, // span - 0x119: 0x6d708, // manifest - 0x11a: 0x38708, // menuitem - 0x11b: 0x58b07, // content - 0x11d: 0x6c109, // onwaiting - 0x11f: 0x4c609, // onloadend - 0x121: 0x37e0d, // oncontextmenu - 0x123: 0x56d06, // onblur - 0x124: 0x3fc07, // article - 0x125: 0x9303, // dir - 0x126: 0xef04, // ping - 0x127: 0x24c08, // required - 0x128: 0x45509, // oninvalid - 0x129: 0xb105, // align - 0x12b: 0x58a04, // icon - 0x12c: 0x64d02, // h6 - 0x12d: 0x1c404, // cols - 0x12e: 0x22e0a, // figcaption - 0x12f: 0x45e09, // onkeydown - 0x130: 0x66b08, // onsubmit - 0x131: 0x14d09, // oncanplay - 0x132: 0x70b03, // sup - 0x133: 0xc01, // p - 0x135: 0x40a09, // onemptied - 0x136: 0x39106, // oncopy - 0x137: 0x19c04, // cite - 0x138: 0x3a70a, // ondblclick - 0x13a: 0x50b0b, // onmousemove - 0x13c: 0x66d03, // sub - 0x13d: 0x48703, // rel - 0x13e: 0x5f08, // optgroup - 0x142: 0x9c07, // rowspan - 0x143: 0x37806, // source - 0x144: 0x21608, // noscript - 0x145: 0x1a304, // open - 0x146: 0x20403, // ins - 0x147: 0x2540d, // foreignObject - 0x148: 0x5ad0a, // onpopstate - 0x14a: 0x28d07, // enctype - 0x14b: 0x2760e, // onautocomplete - 0x14c: 0x35208, // textarea - 0x14e: 0x2780c, // autocomplete - 0x14f: 0x15702, // hr - 0x150: 0x1de08, // controls - 0x151: 0x10902, // id - 0x153: 0x2360c, // onafterprint - 0x155: 0x2610d, // foreignobject - 0x156: 0x32707, // marquee - 0x157: 0x59a07, // onpause - 0x158: 0x5e602, // dl - 0x159: 0x5206, // height - 0x15a: 0x34703, // min - 0x15b: 0x9307, // dirname - 0x15c: 0x1f209, // translate - 0x15d: 0x5604, // html - 0x15e: 0x34709, // minlength - 0x15f: 0x48607, // preload - 0x160: 0x71408, // template - 0x161: 0x3df0b, // ondragleave - 0x162: 0x3a02, // rb - 0x164: 0x5c003, // src - 0x165: 0x6dd06, // strong - 0x167: 0x7804, // samp - 0x168: 0x6f307, // address - 0x169: 0x55108, // ononline - 0x16b: 0x1310b, // placeholder - 0x16c: 0x2c406, // target - 0x16d: 0x20605, // small - 0x16e: 0x6ca07, // onwheel - 0x16f: 0x1c90a, // annotation - 0x170: 0x4740a, // spellcheck - 0x171: 0x7207, // details - 0x172: 0x10306, // canvas - 0x173: 0x12109, // autofocus - 0x174: 0xc05, // param - 0x176: 0x46308, // download - 0x177: 0x45203, // del - 0x178: 0x36c07, // onclose - 0x179: 0xb903, // kbd - 0x17a: 0x31906, // applet - 0x17b: 0x2e004, // href - 0x17c: 0x5f108, // onresize - 0x17e: 0x49d0c, // onloadeddata - 0x180: 0xcc02, // tr - 0x181: 0x2c00a, // formtarget - 0x182: 0x11005, // title - 0x183: 0x6ff05, // style - 0x184: 0xd206, // strike - 0x185: 0x59e06, // usemap - 0x186: 0x2fc06, // iframe - 0x187: 0x1004, // main - 0x189: 0x7b07, // picture - 0x18c: 0x31605, // ismap - 0x18e: 0x4a504, // data - 0x18f: 0x5905, // label - 0x191: 0x3d10e, // referrerpolicy - 0x192: 0x15602, // th - 0x194: 0x53606, // prompt - 0x195: 0x56807, // section - 0x197: 0x6d107, // optimum - 0x198: 0x2db04, // high - 0x199: 0x15c02, // h1 - 0x19a: 0x65909, // onstalled - 0x19b: 0x16d03, // var - 0x19c: 0x4204, // time - 0x19e: 0x67402, // ms - 0x19f: 0x33106, // header - 0x1a0: 0x4da09, // onmessage - 0x1a1: 0x1a605, // nonce - 0x1a2: 0x26e0a, // formaction - 0x1a3: 0x22006, // center - 0x1a4: 0x3704, // nobr - 0x1a5: 0x59505, // table - 0x1a6: 0x4a907, // listing - 0x1a7: 0x18106, // legend - 0x1a9: 0x29b09, // challenge - 0x1aa: 0x24806, // figure - 0x1ab: 0xe605, // media - 0x1ae: 0xd904, // type - 0x1af: 0x3f04, // font - 0x1b0: 0x4da0e, // onmessageerror - 0x1b1: 0x37108, // seamless - 0x1b2: 0x8703, // dfn - 0x1b3: 0x5c705, // defer - 0x1b4: 0xc303, // low - 0x1b5: 0x19a03, // rtc - 0x1b6: 0x5230b, // onmouseover - 0x1b7: 0x2b20a, // novalidate - 0x1b8: 0x71c0a, // workertype - 0x1ba: 0x3cd07, // itemref - 0x1bd: 0x1, // a - 0x1be: 0x31803, // map - 0x1bf: 0x400c, // ontimeupdate - 0x1c0: 0x15e07, // bgsound - 0x1c1: 0x3206, // keygen - 0x1c2: 0x2705, // tbody - 0x1c5: 0x64406, // onshow - 0x1c7: 0x2501, // s - 0x1c8: 0x6607, // pattern - 0x1cc: 0x14d10, // oncanplaythrough - 0x1ce: 0x2d702, // dd - 0x1cf: 0x6f906, // srcset - 0x1d0: 0x17003, // big - 0x1d2: 0x65108, // sortable - 0x1d3: 0x48007, // onkeyup - 0x1d5: 0x5a406, // onplay - 0x1d7: 0x4b804, // meta - 0x1d8: 0x40306, // ondrop - 0x1da: 0x60008, // onscroll - 0x1db: 0x1fb0b, // crossorigin - 0x1dc: 0x5730a, // onpageshow - 0x1dd: 0x4, // abbr - 0x1de: 0x9202, // td - 0x1df: 0x58b0f, // contenteditable - 0x1e0: 0x27206, // action - 0x1e1: 0x1400b, // playsinline - 0x1e2: 0x43107, // onfocus - 0x1e3: 0x2e008, // hreflang - 0x1e5: 0x5160a, // onmouseout - 0x1e6: 0x5ea07, // onreset - 0x1e7: 0x13c08, // autoplay - 0x1e8: 0x63109, // onseeking - 0x1ea: 0x67506, // scoped - 0x1ec: 0x30a, // radiogroup - 0x1ee: 0x3800b, // contextmenu - 0x1ef: 0x52e09, // onmouseup - 0x1f1: 0x2ca06, // hgroup - 0x1f2: 0x2080f, // allowfullscreen - 0x1f3: 0x4be08, // tabindex - 0x1f6: 0x30f07, // isindex - 0x1f7: 0x1a0e, // accept-charset - 0x1f8: 0x2ae0e, // formnovalidate - 0x1fb: 0x1c90e, // annotation-xml - 0x1fc: 0x6e05, // embed - 0x1fd: 0x21806, // script - 0x1fe: 0xbb06, // dialog - 0x1ff: 0x1d707, // command + 0x1: 0x3ff08, // dropzone + 0x2: 0x3b08, // basefont + 0x3: 0x23209, // integrity + 0x4: 0x43106, // source + 0x5: 0x2c09, // accesskey + 0x6: 0x1a06, // accept + 0x7: 0x6c807, // onwheel + 0xb: 0x47407, // onkeyup + 0xc: 0x32007, // headers + 0xd: 0x67306, // scoped + 0xe: 0x67909, // onsuspend + 0xf: 0x8908, // noframes + 0x10: 0x1fa0b, // crossorigin + 0x11: 0x2e407, // onclick + 0x12: 0x3f405, // start + 0x13: 0x37a0b, // contextmenu + 0x14: 0x5e903, // src + 0x15: 0x1c404, // cols + 0x16: 0xbb06, // dialog + 0x17: 0x47a07, // preload + 0x18: 0x3c707, // itemref + 0x1b: 0x2f105, // image + 0x1d: 0x4ba09, // onloadend + 0x1e: 0x45d08, // download + 0x1f: 0x46a03, // pre + 0x23: 0x2970a, // formmethod + 0x24: 0x71303, // svg + 0x25: 0xcf01, // q + 0x26: 0x64002, // dt + 0x27: 0x1de08, // controls + 0x2a: 0x2804, // body + 0x2b: 0xd206, // strike + 0x2c: 0x3910b, // oncuechange + 0x2d: 0x4c30b, // onloadstart + 0x2e: 0x2fe07, // isindex + 0x2f: 0xb202, // li + 0x30: 0x1400b, // playsinline + 0x31: 0x34102, // mi + 0x32: 0x30806, // applet + 0x33: 0x4ce09, // onmessage + 0x35: 0x13702, // ol + 0x36: 0x1a304, // open + 0x39: 0x14d09, // oncanplay + 0x3a: 0x6bf09, // onwaiting + 0x3b: 0x11908, // oncancel + 0x3c: 0x6a908, // onunload + 0x3e: 0x53c09, // onoffline + 0x3f: 0x1a0e, // accept-charset + 0x40: 0x32004, // head + 0x42: 0x3ab09, // ondragend + 0x43: 0x1310b, // placeholder + 0x44: 0x2b30a, // formtarget + 0x45: 0x2540d, // foreignobject + 0x47: 0x400c, // ontimeupdate + 0x48: 0xdd0e, // allowusermedia + 0x4a: 0x69c0d, // onbeforeprint + 0x4b: 0x5604, // html + 0x4c: 0x9f04, // span + 0x4d: 0x64206, // hgroup + 0x4e: 0x16408, // disabled + 0x4f: 0x4204, // time + 0x51: 0x42b07, // onfocus + 0x53: 0xb00a, // malignmark + 0x55: 0x4650a, // onkeypress + 0x56: 0x55805, // class + 0x57: 0x1ab08, // colgroup + 0x58: 0x33709, // maxlength + 0x59: 0x5a908, // progress + 0x5b: 0x70405, // style + 0x5c: 0x2a10e, // formnovalidate + 0x5e: 0x38b06, // oncopy + 0x60: 0x26104, // form + 0x61: 0xf606, // footer + 0x64: 0x30a, // radiogroup + 0x66: 0xfb04, // ruby + 0x67: 0x4ff0b, // onmousemove + 0x68: 0x19d08, // itemprop + 0x69: 0x2d70a, // http-equiv + 0x6a: 0x15602, // th + 0x6c: 0x6e02, // em + 0x6d: 0x38108, // menuitem + 0x6e: 0x63106, // select + 0x6f: 0x48110, // onlanguagechange + 0x70: 0x31f05, // thead + 0x71: 0x15c02, // h1 + 0x72: 0x5e906, // srcdoc + 0x75: 0x9604, // name + 0x76: 0x19106, // button + 0x77: 0x55504, // desc + 0x78: 0x17704, // kind + 0x79: 0x1bf05, // color + 0x7c: 0x58e06, // usemap + 0x7d: 0x30e08, // itemtype + 0x7f: 0x6d508, // manifest + 0x81: 0x5300c, // onmousewheel + 0x82: 0x4dc0b, // onmousedown + 0x84: 0xc05, // param + 0x85: 0x2e005, // video + 0x86: 0x4910c, // onloadeddata + 0x87: 0x6f107, // address + 0x8c: 0xef04, // ping + 0x8d: 0x24703, // for + 0x8f: 0x62f08, // onselect + 0x90: 0x30703, // map + 0x92: 0xc01, // p + 0x93: 0x8008, // reversed + 0x94: 0x54d0a, // onpagehide + 0x95: 0x3206, // keygen + 0x96: 0x34109, // minlength + 0x97: 0x3e40a, // ondragover + 0x98: 0x42407, // onerror + 0x9a: 0x2107, // charset + 0x9b: 0x29b06, // method + 0x9c: 0x101, // b + 0x9d: 0x68208, // ontoggle + 0x9e: 0x2bd06, // hidden + 0xa0: 0x3f607, // article + 0xa2: 0x63906, // onshow + 0xa3: 0x64d06, // onsort + 0xa5: 0x57b0f, // contenteditable + 0xa6: 0x66908, // onsubmit + 0xa8: 0x44f09, // oninvalid + 0xaa: 0x202, // br + 0xab: 0x10902, // id + 0xac: 0x5d04, // loop + 0xad: 0x5630a, // onpageshow + 0xb0: 0x2cf04, // href + 0xb2: 0x2210a, // figcaption + 0xb3: 0x2690e, // onautocomplete + 0xb4: 0x49106, // onload + 0xb6: 0x9c04, // rows + 0xb7: 0x1a605, // nonce + 0xb8: 0x68a14, // onunhandledrejection + 0xbb: 0x21306, // center + 0xbc: 0x59406, // onplay + 0xbd: 0x33f02, // h5 + 0xbe: 0x49d07, // listing + 0xbf: 0x57606, // public + 0xc2: 0x23b06, // figure + 0xc3: 0x57a04, // icon + 0xc4: 0x1ab03, // col + 0xc5: 0x47b03, // rel + 0xc6: 0xe605, // media + 0xc7: 0x12109, // autofocus + 0xc8: 0x19a02, // rt + 0xca: 0x2d304, // lang + 0xcc: 0x49908, // datalist + 0xce: 0x2eb06, // iframe + 0xcf: 0x36105, // muted + 0xd0: 0x6140a, // onauxclick + 0xd2: 0x3c02, // as + 0xd6: 0x3fd06, // ondrop + 0xd7: 0x1c90a, // annotation + 0xd8: 0x21908, // fieldset + 0xdb: 0x2cf08, // hreflang + 0xdc: 0x4e70c, // onmouseenter + 0xdd: 0x2a402, // mn + 0xde: 0xe60a, // mediagroup + 0xdf: 0x9805, // meter + 0xe0: 0x56c03, // wbr + 0xe2: 0x63e05, // width + 0xe3: 0x2290c, // onafterprint + 0xe4: 0x30505, // ismap + 0xe5: 0x1505, // value + 0xe7: 0x1303, // nav + 0xe8: 0x54508, // ononline + 0xe9: 0xb604, // mark + 0xea: 0xc303, // low + 0xeb: 0x3ee0b, // ondragstart + 0xef: 0x12f03, // xmp + 0xf0: 0x22407, // caption + 0xf1: 0xd904, // type + 0xf2: 0x70907, // summary + 0xf3: 0x6802, // tt + 0xf4: 0x20809, // translate + 0xf5: 0x1870a, // blockquote + 0xf8: 0x15702, // hr + 0xfa: 0x2705, // tbody + 0xfc: 0x7b07, // picture + 0xfd: 0x5206, // height + 0xfe: 0x19c04, // cite + 0xff: 0x2501, // s + 0x101: 0xff05, // async + 0x102: 0x56f07, // onpaste + 0x103: 0x19507, // onabort + 0x104: 0x2b706, // target + 0x105: 0x14b03, // bdo + 0x106: 0x1f006, // coords + 0x107: 0x5e108, // onresize + 0x108: 0x71908, // template + 0x10a: 0x3a02, // rb + 0x10b: 0x2a50a, // novalidate + 0x10c: 0x460e, // updateviacache + 0x10d: 0x71003, // sup + 0x10e: 0x6c07, // noembed + 0x10f: 0x16b03, // div + 0x110: 0x6f707, // srclang + 0x111: 0x17a09, // draggable + 0x112: 0x67305, // scope + 0x113: 0x5905, // label + 0x114: 0x22f02, // rp + 0x115: 0x23f08, // required + 0x116: 0x3780d, // oncontextmenu + 0x117: 0x5e504, // size + 0x118: 0x5b00a, // spellcheck + 0x119: 0x3f04, // font + 0x11a: 0x9c07, // rowspan + 0x11b: 0x10a07, // default + 0x11d: 0x44307, // oninput + 0x11e: 0x38506, // itemid + 0x11f: 0x5ee04, // code + 0x120: 0xaa07, // acronym + 0x121: 0x3b04, // base + 0x125: 0x2470d, // foreignObject + 0x126: 0x2ca04, // high + 0x127: 0x3cb0e, // referrerpolicy + 0x128: 0x33703, // max + 0x129: 0x59d0a, // onpopstate + 0x12a: 0x2fc02, // h4 + 0x12b: 0x4ac04, // meta + 0x12c: 0x17305, // blink + 0x12e: 0x5f508, // onscroll + 0x12f: 0x59409, // onplaying + 0x130: 0xc113, // allowpaymentrequest + 0x131: 0x19a03, // rtc + 0x132: 0x72b04, // wrap + 0x134: 0x8b08, // frameset + 0x135: 0x32605, // small + 0x137: 0x32006, // header + 0x138: 0x40409, // onemptied + 0x139: 0x34902, // h6 + 0x13a: 0x35908, // multiple + 0x13c: 0x52a06, // prompt + 0x13f: 0x28e09, // challenge + 0x141: 0x4370c, // onhashchange + 0x142: 0x57b07, // content + 0x143: 0x1c90e, // annotation-xml + 0x144: 0x36607, // onclose + 0x145: 0x14d10, // oncanplaythrough + 0x148: 0x5170b, // onmouseover + 0x149: 0x64f08, // sortable + 0x14a: 0xa402, // mo + 0x14b: 0x2cd02, // h3 + 0x14c: 0x2c406, // script + 0x14d: 0x41d07, // onended + 0x14f: 0x64706, // poster + 0x150: 0x7210a, // workertype + 0x153: 0x1f505, // shape + 0x154: 0x4, // abbr + 0x155: 0x1, // a + 0x156: 0x2bf02, // dd + 0x157: 0x71606, // system + 0x158: 0x4ce0e, // onmessageerror + 0x159: 0x36b08, // seamless + 0x15a: 0x2610a, // formaction + 0x15b: 0x6e106, // option + 0x15c: 0x31d04, // math + 0x15d: 0x62609, // onseeking + 0x15e: 0x39c05, // oncut + 0x15f: 0x44c03, // del + 0x160: 0x11005, // title + 0x161: 0x11505, // audio + 0x162: 0x63108, // selected + 0x165: 0x3b40b, // ondragenter + 0x166: 0x46e06, // spacer + 0x167: 0x4a410, // onloadedmetadata + 0x168: 0x44505, // input + 0x16a: 0x58505, // table + 0x16b: 0x41508, // onchange + 0x16e: 0x5f005, // defer + 0x171: 0x50a0a, // onmouseout + 0x172: 0x20504, // slot + 0x175: 0x3704, // nobr + 0x177: 0x1d707, // command + 0x17a: 0x7207, // details + 0x17b: 0x38104, // menu + 0x17c: 0xb903, // kbd + 0x17d: 0x57304, // step + 0x17e: 0x20303, // ins + 0x17f: 0x13c08, // autoplay + 0x182: 0x34103, // min + 0x183: 0x17404, // link + 0x185: 0x40d10, // ondurationchange + 0x186: 0x9202, // td + 0x187: 0x8b05, // frame + 0x18a: 0x2ab08, // datetime + 0x18b: 0x44509, // inputmode + 0x18c: 0x35108, // readonly + 0x18d: 0x21104, // face + 0x18f: 0x5e505, // sizes + 0x191: 0x4b208, // tabindex + 0x192: 0x6db06, // strong + 0x193: 0xba03, // bdi + 0x194: 0x6fe06, // srcset + 0x196: 0x67202, // ms + 0x197: 0x5b507, // checked + 0x198: 0xb105, // align + 0x199: 0x1e507, // section + 0x19b: 0x6e05, // embed + 0x19d: 0x15e07, // bgsound + 0x1a2: 0x49d04, // list + 0x1a3: 0x61e08, // onseeked + 0x1a4: 0x66009, // onstorage + 0x1a5: 0x2f603, // img + 0x1a6: 0xf505, // tfoot + 0x1a9: 0x26913, // onautocompleteerror + 0x1aa: 0x5fd19, // onsecuritypolicyviolation + 0x1ad: 0x9303, // dir + 0x1ae: 0x9307, // dirname + 0x1b0: 0x5a70a, // onprogress + 0x1b2: 0x65709, // onstalled + 0x1b5: 0x66f09, // itemscope + 0x1b6: 0x49904, // data + 0x1b7: 0x3d90b, // ondragleave + 0x1b8: 0x56102, // h2 + 0x1b9: 0x2f706, // mglyph + 0x1ba: 0x16502, // is + 0x1bb: 0x6e50e, // onbeforeunload + 0x1bc: 0x2830d, // typemustmatch + 0x1bd: 0x3ab06, // ondrag + 0x1be: 0x5da07, // onreset + 0x1c0: 0x51106, // output + 0x1c1: 0x12907, // sandbox + 0x1c2: 0x1b209, // plaintext + 0x1c4: 0x34c08, // textarea + 0x1c7: 0xd607, // keytype + 0x1c8: 0x34b05, // mtext + 0x1c9: 0x6b10e, // onvolumechange + 0x1ca: 0x1ea06, // onblur + 0x1cb: 0x58a07, // onpause + 0x1cd: 0x5bc0c, // onratechange + 0x1ce: 0x10705, // aside + 0x1cf: 0x6cf07, // optimum + 0x1d1: 0x45809, // onkeydown + 0x1d2: 0x1c407, // colspan + 0x1d3: 0x1004, // main + 0x1d4: 0x66b03, // sub + 0x1d5: 0x25b06, // object + 0x1d6: 0x55c06, // search + 0x1d7: 0x37206, // sorted + 0x1d8: 0x17003, // big + 0x1d9: 0xb01, // u + 0x1db: 0x26b0c, // autocomplete + 0x1dc: 0xcc02, // tr + 0x1dd: 0xf303, // alt + 0x1df: 0x7804, // samp + 0x1e0: 0x5c812, // onrejectionhandled + 0x1e1: 0x4f30c, // onmouseleave + 0x1e2: 0x28007, // enctype + 0x1e3: 0xa208, // nomodule + 0x1e5: 0x3280f, // allowfullscreen + 0x1e6: 0x5f08, // optgroup + 0x1e8: 0x27c0b, // formenctype + 0x1e9: 0x18106, // legend + 0x1ea: 0x10306, // canvas + 0x1eb: 0x6607, // pattern + 0x1ec: 0x2c208, // noscript + 0x1ed: 0x601, // i + 0x1ee: 0x5d602, // dl + 0x1ef: 0xa702, // ul + 0x1f2: 0x52209, // onmouseup + 0x1f4: 0x1ba05, // track + 0x1f7: 0x3a10a, // ondblclick + 0x1f8: 0x3bf0a, // ondragexit + 0x1fa: 0x8703, // dfn + 0x1fc: 0x26506, // action + 0x1fd: 0x35004, // area + 0x1fe: 0x31607, // marquee + 0x1ff: 0x16d03, // var } const atomText = "abbradiogrouparamainavalueaccept-charsetbodyaccesskeygenobrb" + @@ -758,26 +760,26 @@ const atomText = "abbradiogrouparamainavalueaccept-charsetbodyaccesskeygenobrb" "dboxmplaceholderautoplaysinlinebdoncanplaythrough1bgsoundisa" + "bledivarbigblinkindraggablegendblockquotebuttonabortcitempro" + "penoncecolgrouplaintextrackcolorcolspannotation-xmlcommandco" + - "ntrolshapecoordslotranslatecrossoriginsmallowfullscreenoscri" + - "ptfacenterfieldsetfigcaptionafterprintegrityfigurequiredfore" + - "ignObjectforeignobjectformactionautocompleteerrorformenctype" + - "mustmatchallengeformmethodformnovalidatetimeformtargethgroup" + - "osterhiddenhigh2hreflanghttp-equivideonclickiframeimageimgly" + - "ph3isindexismappletitemtypemarqueematheadersortedmaxlength4m" + - "inlength5mtextareadonlymultiplemutedoncloseamlessourceoncont" + - "extmenuitemidoncopyoncuechangeoncutondblclickondragendondrag" + - "enterondragexitemreferrerpolicyondragleaveondragoverondragst" + - "articleondropzonemptiedondurationchangeonendedonerroronfocus" + - "paceronhashchangeoninputmodeloninvalidonkeydownloadonkeypres" + - "spellcheckedonkeyupreloadonlanguagechangeonloadeddatalisting" + - "onloadedmetadatabindexonloadendonloadstartonmessageerroronmo" + - "usedownonmouseenteronmouseleaveonmousemoveonmouseoutputonmou" + - "seoveronmouseupromptonmousewheelonofflineononlineonpagehides" + - "classectionbluronpageshowbronpastepublicontenteditableonpaus" + - "emaponplayingonpopstateonprogressrcdocodeferonratechangeonre" + - "jectionhandledonresetonresizesrclangonscrollonsecuritypolicy" + - "violationauxclickonseekedonseekingonselectedonshowidth6onsor" + - "tableonstalledonstorageonsubmitemscopedonsuspendontoggleonun" + - "handledrejectionbeforeprintonunloadonvolumechangeonwaitingon" + - "wheeloptimumanifestrongoptionbeforeunloaddressrcsetstylesumm" + - "arysupsvgsystemplateworkertypewrap" + "ntrolsectionblurcoordshapecrossoriginslotranslatefacenterfie" + + "ldsetfigcaptionafterprintegrityfigurequiredforeignObjectfore" + + "ignobjectformactionautocompleteerrorformenctypemustmatchalle" + + "ngeformmethodformnovalidatetimeformtargethiddenoscripthigh3h" + + "reflanghttp-equivideonclickiframeimageimglyph4isindexismappl" + + "etitemtypemarqueematheadersmallowfullscreenmaxlength5minleng" + + "th6mtextareadonlymultiplemutedoncloseamlessortedoncontextmen" + + "uitemidoncopyoncuechangeoncutondblclickondragendondragentero" + + "ndragexitemreferrerpolicyondragleaveondragoverondragstarticl" + + "eondropzonemptiedondurationchangeonendedonerroronfocusourceo" + + "nhashchangeoninputmodeloninvalidonkeydownloadonkeypresspacer" + + "onkeyupreloadonlanguagechangeonloadeddatalistingonloadedmeta" + + "databindexonloadendonloadstartonmessageerroronmousedownonmou" + + "seenteronmouseleaveonmousemoveonmouseoutputonmouseoveronmous" + + "eupromptonmousewheelonofflineononlineonpagehidesclassearch2o" + + "npageshowbronpastepublicontenteditableonpausemaponplayingonp" + + "opstateonprogresspellcheckedonratechangeonrejectionhandledon" + + "resetonresizesrcdocodeferonscrollonsecuritypolicyviolationau" + + "xclickonseekedonseekingonselectedonshowidthgrouposteronsorta" + + "bleonstalledonstorageonsubmitemscopedonsuspendontoggleonunha" + + "ndledrejectionbeforeprintonunloadonvolumechangeonwaitingonwh" + + "eeloptimumanifestrongoptionbeforeunloaddressrclangsrcsetstyl" + + "esummarysupsvgsystemplateworkertypewrap" diff --git a/vendor/golang.org/x/net/html/entity.go b/vendor/golang.org/x/net/html/entity.go index b628880a0..4e8d5d55f 100644 --- a/vendor/golang.org/x/net/html/entity.go +++ b/vendor/golang.org/x/net/html/entity.go @@ -2156,9 +2156,8 @@ var entity = map[string]rune{ // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ - // TODO(nigeltao): Handle replacements that are wider than their names. - // "nLt;": {'\u226A', '\u20D2'}, - // "nGt;": {'\u226B', '\u20D2'}, + "nLt;": {'\u226A', '\u20D2'}, + "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, diff --git a/vendor/golang.org/x/net/html/escape.go b/vendor/golang.org/x/net/html/escape.go index 04c6bec21..df3edc5b1 100644 --- a/vendor/golang.org/x/net/html/escape.go +++ b/vendor/golang.org/x/net/html/escape.go @@ -6,6 +6,7 @@ package html import ( "bytes" + "slices" "strings" "unicode/utf8" ) @@ -50,25 +51,24 @@ var replacementTable = [...]rune{ // 0x0D->'\u000D' is a no-op. } -// unescapeEntity reads an entity like "<" from b[src:] and writes the -// corresponding "<" to b[dst:], returning the incremented dst and src cursors. -// Precondition: b[src] == '&' && dst <= src. -// attribute should be true if parsing an attribute value. -func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { +// unescapeEntity attempts to consume a character reference from s[src:], +// returning the rune, potential second rune, and number of bytes consumed +// (which indicates the length of the character reference). It is assumed that +// the first byte of s is '&'. attribute should be true if parsing an attribute +// value. +func unescapeEntity(s []byte, attribute bool) (rune, rune, int) { // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. - i, s := 1, b[src:] + i := 1 if len(s) <= 1 { - b[dst] = b[src] - return dst + 1, src + 1 + return '&', 0, 1 } if s[i] == '#' { - if len(s) <= 3 { // We need to have at least "&#.". - b[dst] = b[src] - return dst + 1, src + 1 + if len(s) <= 2 { // We need to have at least "&#". + return '&', 0, 1 } i++ c := s[i] @@ -78,34 +78,43 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { i++ } + i0 := i x := '\x00' for i < len(s) { c = s[i] - i++ + var d rune + var mult rune if hex { + mult = 16 if '0' <= c && c <= '9' { - x = 16*x + rune(c) - '0' - continue + d = rune(c) - '0' } else if 'a' <= c && c <= 'f' { - x = 16*x + rune(c) - 'a' + 10 - continue + d = rune(c) - 'a' + 10 } else if 'A' <= c && c <= 'F' { - x = 16*x + rune(c) - 'A' + 10 - continue + d = rune(c) - 'A' + 10 + } else { + break + } + } else { + mult = 10 + if '0' <= c && c <= '9' { + d = rune(c) - '0' + } else { + break } - } else if '0' <= c && c <= '9' { - x = 10*x + rune(c) - '0' - continue } - if c != ';' { - i-- + if x <= 0x10FFFF { + x = mult*x + d } - break + i++ + } + + if i == i0 { // No characters matched. + return '&', 0, 1 } - if i <= 3 { // No characters matched. - b[dst] = b[src] - return dst + 1, src + 1 + if i < len(s) && s[i] == ';' { + i++ } if 0x80 <= x && x <= 0x9F { @@ -116,7 +125,7 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { x = '\uFFFD' } - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } // Consume the maximum number of characters possible, with the @@ -141,10 +150,9 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } else if x := entity2[entityName]; x[0] != 0 { - dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) - return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i + return x[0], x[1], i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { @@ -152,35 +160,67 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 + return x, 0, j + 1 } } } - dst1, src1 = dst+i, src+i - copy(b[dst:dst1], b[src:src1]) - return dst1, src1 + return '&', 0, 1 } -// unescape unescapes b's entities in-place, so that "a<b" becomes "a entityNameLen { + if reusingB { + out = slices.Clone(out) + reusingB = false } - return b[0:dst] + out = slices.Grow(out, replLen) + } + out = utf8.AppendRune(out, r1) + if r2 != 0 { + out = utf8.AppendRune(out, r2) } + + src += entityNameLen } - return b + + return out } // lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc". @@ -299,7 +339,7 @@ func escape(w writer, s string) error { case '\r': esc = " " default: - panic("unrecognized escape character") + panic("html: unrecognized escape character") } s = s[i+1:] if _, err := w.WriteString(esc); err != nil { diff --git a/vendor/golang.org/x/net/html/foreign.go b/vendor/golang.org/x/net/html/foreign.go index e8515d8e8..65d01d1ed 100644 --- a/vendor/golang.org/x/net/html/foreign.go +++ b/vendor/golang.org/x/net/html/foreign.go @@ -23,7 +23,7 @@ func adjustForeignAttributes(aa []Attribute) { } switch a.Key { case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", - "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": + "xlink:title", "xlink:type", "xml:lang", "xml:space", "xmlns:xlink": j := strings.Index(a.Key, ":") aa[i].Namespace = a.Key[:j] aa[i].Key = a.Key[j+1:] diff --git a/vendor/golang.org/x/net/html/iter.go b/vendor/golang.org/x/net/html/iter.go index 54be8fd30..349ef73e6 100644 --- a/vendor/golang.org/x/net/html/iter.go +++ b/vendor/golang.org/x/net/html/iter.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.23 - package html import "iter" diff --git a/vendor/golang.org/x/net/html/node.go b/vendor/golang.org/x/net/html/node.go index 77741a195..253e4679c 100644 --- a/vendor/golang.org/x/net/html/node.go +++ b/vendor/golang.org/x/net/html/node.go @@ -11,6 +11,7 @@ import ( // A NodeType is the type of a Node. type NodeType uint32 +//go:generate stringer -type NodeType const ( ErrorNode NodeType = iota TextNode diff --git a/vendor/golang.org/x/net/html/nodetype_string.go b/vendor/golang.org/x/net/html/nodetype_string.go new file mode 100644 index 000000000..8253af491 --- /dev/null +++ b/vendor/golang.org/x/net/html/nodetype_string.go @@ -0,0 +1,31 @@ +// Code generated by "stringer -type NodeType"; DO NOT EDIT. + +package html + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ErrorNode-0] + _ = x[TextNode-1] + _ = x[DocumentNode-2] + _ = x[ElementNode-3] + _ = x[CommentNode-4] + _ = x[DoctypeNode-5] + _ = x[RawNode-6] + _ = x[scopeMarkerNode-7] +} + +const _NodeType_name = "ErrorNodeTextNodeDocumentNodeElementNodeCommentNodeDoctypeNodeRawNodescopeMarkerNode" + +var _NodeType_index = [...]uint8{0, 9, 17, 29, 40, 51, 62, 69, 84} + +func (i NodeType) String() string { + idx := int(i) - 0 + if i < 0 || idx >= len(_NodeType_index)-1 { + return "NodeType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _NodeType_name[_NodeType_index[idx]:_NodeType_index[idx+1]] +} diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index 643c674e3..165b6108d 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -5,9 +5,11 @@ package html import ( + "cmp" "errors" "fmt" "io" + "slices" "strings" a "golang.org/x/net/html/atom" @@ -61,7 +63,7 @@ func (p *parser) top() *Node { // Stop tags for use in popUntil. These come from section 12.2.4.2. var ( defaultScopeStopTags = map[string][]a.Atom{ - "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, + "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template, a.Select}, "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, "svg": {a.Desc, a.ForeignObject, a.Title}, } @@ -76,7 +78,6 @@ const ( tableScope tableRowScope tableBodyScope - selectScope ) // popUntil pops the stack of open elements at the highest element whose tag @@ -131,12 +132,8 @@ func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { return -1 } - case selectScope: - if tagAtom != a.Optgroup && tagAtom != a.Option { - return -1 - } default: - panic("unreachable") + panic(fmt.Sprintf("html: internal error: indexOfElementInScope unknown scope: %d", s)) } } switch s { @@ -179,7 +176,7 @@ func (p *parser) clearStackToContext(s scope) { return } default: - panic("unreachable") + panic(fmt.Sprintf("html: internal error: clearStackToContext unknown scope: %d", s)) } } } @@ -231,7 +228,14 @@ func (p *parser) addChild(n *Node) { } if n.Type == ElementNode { - p.oe = append(p.oe, n) + p.insertOpenElement(n) + } +} + +func (p *parser) insertOpenElement(n *Node) { + p.oe = append(p.oe, n) + if len(p.oe) > 512 { + panic("html: open stack of elements exceeds 512 nodes") } } @@ -321,6 +325,14 @@ func (p *parser) addText(text string) { }) } +func attrCompare(a, b Attribute) int { + return cmp.Or( + cmp.Compare(a.Namespace, b.Namespace), + cmp.Compare(a.Key, b.Key), + cmp.Compare(a.Val, b.Val), + ) +} + // addElement adds a child element based on the current token. func (p *parser) addElement() { p.addChild(&Node{ @@ -336,6 +348,10 @@ func (p *parser) addFormattingElement() { tagAtom, attr := p.tok.DataAtom, p.tok.Attr p.addElement() + // In order to optimize the search, we need the attributes to be sorted, so we + // can just use slices.Equal. + slices.SortFunc(attr, attrCompare) + // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0 findIdenticalElements: @@ -353,19 +369,7 @@ findIdenticalElements: if n.DataAtom != tagAtom { continue } - if len(n.Attr) != len(attr) { - continue - } - compareAttributes: - for _, t0 := range n.Attr { - for _, t1 := range attr { - if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val { - // Found a match for this attribute, continue with the next attribute. - continue compareAttributes - } - } - // If we get here, there is no attribute that matches a. - // Therefore the element is not identical to the new one. + if !slices.Equal(n.Attr, attr) { continue findIdenticalElements } @@ -375,7 +379,11 @@ findIdenticalElements: } } - p.afe = append(p.afe, p.top()) + // Sort the attributes to optimize future identical-element searches. + top := p.top() + slices.SortFunc(top.Attr, attrCompare) + + p.afe = append(p.afe, top) } // Section 12.2.4.3. @@ -447,21 +455,6 @@ func (p *parser) resetInsertionMode() { } switch n.DataAtom { - case a.Select: - if !last { - for ancestor, first := n, p.oe[0]; ancestor != first; { - ancestor = p.oe[p.oe.index(ancestor)-1] - switch ancestor.DataAtom { - case a.Template: - p.im = inSelectIM - return - case a.Table: - p.im = inSelectInTableIM - return - } - } - } - p.im = inSelectIM case a.Td, a.Th: // TODO: remove this divergence from the HTML5 spec. // @@ -810,7 +803,7 @@ func afterHeadIM(p *parser) bool { p.im = inFramesetIM return true case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title: - p.oe = append(p.oe, p.head) + p.insertOpenElement(p.head) defer p.oe.remove(p.head) return inHeadIM(p) case a.Head: @@ -924,7 +917,7 @@ func inBodyIM(p *parser) bool { p.addElement() p.im = inFramesetIM return true - case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Main, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul: + case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Main, a.Menu, a.Nav, a.Ol, a.P, a.Search, a.Section, a.Summary, a.Ul: p.popUntil(buttonScope, a.P) p.addElement() case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6: @@ -989,7 +982,10 @@ func inBodyIM(p *parser) bool { p.popUntil(buttonScope, a.P) p.addElement() case a.Button: - p.popUntil(defaultScope, a.Button) + if p.elementInScope(defaultScope, a.Button) { + p.generateImpliedEndTags() + p.popUntil(defaultScope, a.Button) + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false @@ -1027,7 +1023,18 @@ func inBodyIM(p *parser) bool { p.framesetOK = false p.im = inTableIM return true - case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr: + case a.Area, a.Br, a.Embed, a.Img, a.Keygen, a.Wbr: + p.reconstructActiveFormattingElements() + p.addElement() + p.oe.pop() + p.acknowledgeSelfClosingTag() + p.framesetOK = false + case a.Input: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } + p.popUntil(defaultScope, a.Select) p.reconstructActiveFormattingElements() p.addElement() p.oe.pop() @@ -1048,7 +1055,13 @@ func inBodyIM(p *parser) bool { p.oe.pop() p.acknowledgeSelfClosingTag() case a.Hr: - p.popUntil(buttonScope, a.P) + if p.elementInScope(buttonScope, a.P) { + p.generateImpliedEndTags("p") + p.popUntil(defaultScope, a.P) + } + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + } p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() @@ -1082,13 +1095,30 @@ func inBodyIM(p *parser) bool { // Don't let the tokenizer go into raw text mode when scripting is disabled. p.tokenizer.NextIsNotRawText() case a.Select: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } else if p.popUntil(defaultScope, a.Select) { + return true + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false - p.im = inSelectIM return true - case a.Optgroup, a.Option: - if p.top().DataAtom == a.Option { + case a.Option: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags("optgroup") + // If oe has option element in scope, parse error? + } else if p.top().DataAtom == a.Option { + p.oe.pop() + } + p.reconstructActiveFormattingElements() + p.addElement() + case a.Optgroup: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + // If oe has option or optgroup element in scope, parse error? + } else if p.top().DataAtom == a.Option { p.oe.pop() } p.reconstructActiveFormattingElements() @@ -1136,7 +1166,12 @@ func inBodyIM(p *parser) bool { return false } return true - case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul: + case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Select, a.Summary, a.Ul: + if !p.elementInScope(defaultScope, p.tok.DataAtom) { + // Ignore the token. + return true + } + p.generateImpliedEndTags() p.popUntil(defaultScope, p.tok.DataAtom) case a.Form: if p.oe.contains(a.Template) { @@ -1365,8 +1400,6 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom, tagName string) { } // inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM. -// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content -// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) { for i := len(p.oe) - 1; i >= 0; i-- { // Two element nodes have the same tag if they have the same Data (a @@ -1376,7 +1409,7 @@ func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) { // Uncommon (custom) tags get a zero DataAtom. // // The if condition here is equivalent to (p.oe[i].Data == tagName). - if (p.oe[i].DataAtom == tagAtom) && + if p.oe[i].Namespace == "" && (p.oe[i].DataAtom == tagAtom) && ((tagAtom != 0) || (p.oe[i].Data == tagName)) { p.oe = p.oe[:i] break @@ -1477,17 +1510,6 @@ func inTableIM(p *parser) bool { } p.addElement() p.form = p.oe.pop() - case a.Select: - p.reconstructActiveFormattingElements() - switch p.top().DataAtom { - case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: - p.fosterParenting = true - } - p.addElement() - p.fosterParenting = false - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1536,12 +1558,6 @@ func inCaptionIM(p *parser) bool { p.clearActiveFormattingElements() p.im = inTableIM return false - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1678,7 +1694,7 @@ func inTableBodyIM(p *parser) bool { return inTableIM(p) } -// Section 12.2.6.4.14. +// Section 13.2.6.4.14. func inRowIM(p *parser) bool { switch p.tok.Type { case StartTagToken: @@ -1690,7 +1706,9 @@ func inRowIM(p *parser) bool { p.im = inCellIM return true case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead, a.Tr: - if p.popUntil(tableScope, a.Tr) { + if p.elementInScope(tableScope, a.Tr) { + p.clearStackToContext(tableRowScope) + p.oe.pop() p.im = inTableBodyIM return false } @@ -1700,22 +1718,28 @@ func inRowIM(p *parser) bool { case EndTagToken: switch p.tok.DataAtom { case a.Tr: - if p.popUntil(tableScope, a.Tr) { + if p.elementInScope(tableScope, a.Tr) { + p.clearStackToContext(tableRowScope) + p.oe.pop() p.im = inTableBodyIM return true } // Ignore the token. return true case a.Table: - if p.popUntil(tableScope, a.Tr) { + if p.elementInScope(tableScope, a.Tr) { + p.clearStackToContext(tableRowScope) + p.oe.pop() p.im = inTableBodyIM return false } // Ignore the token. return true case a.Tbody, a.Tfoot, a.Thead: - if p.elementInScope(tableScope, p.tok.DataAtom) { - p.parseImpliedToken(EndTagToken, a.Tr, a.Tr.String()) + if p.elementInScope(tableScope, p.tok.DataAtom) && p.elementInScope(tableScope, a.Tr) { + p.clearStackToContext(tableRowScope) + p.oe.pop() + p.im = inTableBodyIM return false } // Ignore the token. @@ -1743,12 +1767,6 @@ func inCellIM(p *parser) bool { } // Ignore the token. return true - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1779,118 +1797,6 @@ func inCellIM(p *parser) bool { return inBodyIM(p) } -// Section 12.2.6.4.16. -func inSelectIM(p *parser) bool { - switch p.tok.Type { - case TextToken: - p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) - case StartTagToken: - switch p.tok.DataAtom { - case a.Html: - return inBodyIM(p) - case a.Option: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - p.addElement() - case a.Optgroup: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - if p.top().DataAtom == a.Optgroup { - p.oe.pop() - } - p.addElement() - case a.Select: - if !p.popUntil(selectScope, a.Select) { - // Ignore the token. - return true - } - p.resetInsertionMode() - case a.Input, a.Keygen, a.Textarea: - if p.elementInScope(selectScope, a.Select) { - p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) - return false - } - // In order to properly ignore