@autorest/go
Version:
AutoRest Go Generator
542 lines (501 loc) • 15.7 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { values } from '@azure-tools/linq';
import { contentPreamble, getSerDeFormat, recursiveUnwrapMapSlice } from './helpers.js';
import { ImportManager } from './imports.js';
import { CodegenError } from './errors.js';
// represents the generated content for an operation group
export class Content {
name;
content;
constructor(name, content) {
this.name = name;
this.content = content;
}
}
// Creates the content for required time marshalling helpers.
// Will be empty if no helpers are required.
export async function generateTimeHelpers(codeModel, packageName) {
let needsDateHelper = false;
let needsDateTimeRFC1123Helper = false;
let needsDateTimeRFC3339Helper = false;
let needsTimeRFC3339Helper = false;
let needsUnixTimeHelper = false;
const setHelper = function (dateTimeFormat) {
switch (dateTimeFormat) {
case 'dateTimeRFC1123':
needsDateTimeRFC1123Helper = true;
break;
case 'dateTimeRFC3339':
needsDateTimeRFC3339Helper = true;
break;
case 'dateType':
needsDateHelper = true;
break;
case 'timeRFC3339':
needsTimeRFC3339Helper = true;
break;
case 'timeUnix':
needsUnixTimeHelper = true;
break;
default:
throw new CodegenError('InternalError', `unhandled date-time format ${dateTimeFormat}`);
}
};
// needsSerDeHelpers is only required when time.Time is a struct field
let needsSerDeHelpers = false;
// find the required helpers.
// for most packages, we must check params, response envelopes, and models
// for fakes, we check a subset
if (packageName !== 'fake') {
for (const client of codeModel.clients) {
for (const method of client.methods) {
for (const param of method.parameters) {
const unwrappedParam = recursiveUnwrapMapSlice(param.type);
if (unwrappedParam.kind !== 'time') {
continue;
}
// for body params, the helpers are always required.
// for header/path/query params, the conversion happens in place. the only
// exceptions are for timeRFC3339 and timeUnix
// TODO: clean this up when moving to DateTime type in azcore
if (param.kind === 'bodyParam' || unwrappedParam.format === 'timeRFC3339' || unwrappedParam.format === 'timeUnix') {
setHelper(unwrappedParam.format);
}
}
}
}
for (const model of codeModel.models) {
for (const field of values(model.fields)) {
const unwrappedField = recursiveUnwrapMapSlice(field.type);
if (unwrappedField.kind !== 'time') {
continue;
}
if (getSerDeFormat(model, codeModel) === 'JSON') {
// needsSerDeHelpers helpers are for JSON only
needsSerDeHelpers = true;
}
setHelper(unwrappedField.format);
}
}
for (const respEnv of codeModel.responseEnvelopes) {
if (!respEnv.result || respEnv.result.kind !== 'monomorphicResult' || respEnv.result.format !== 'JSON') {
continue;
}
const unwrappedResult = recursiveUnwrapMapSlice(respEnv.result.monomorphicType);
if (unwrappedResult.kind !== 'time') {
continue;
}
setHelper(unwrappedResult.format);
}
}
else {
// for fakes, only need to check the if the body params are of type time.Time.
// otherwise, the conversion happens in place
for (const client of codeModel.clients) {
for (const method of client.methods) {
for (const param of method.parameters) {
if (param.kind === 'bodyParam' && param.type.kind === 'time') {
setHelper(param.type.format);
}
}
}
}
for (const respEnv of codeModel.responseEnvelopes) {
for (const header of respEnv.headers) {
// for header/path/query params, the conversion happens in place. the only
// exceptions are for timeRFC3339 and timeUnix
if (header.type.kind === 'time' && (header.type.format === 'timeRFC3339' || header.type.format === 'timeUnix')) {
setHelper(header.type.format);
}
}
if (respEnv.result?.kind === 'monomorphicResult' && respEnv.result.monomorphicType.kind === 'time') {
setHelper(respEnv.result.monomorphicType.format);
}
}
}
const content = new Array();
if (!needsDateHelper &&
!needsDateTimeRFC1123Helper &&
!needsDateTimeRFC3339Helper &&
!needsTimeRFC3339Helper &&
!needsUnixTimeHelper) {
return content;
}
const preamble = contentPreamble(codeModel, true, packageName);
if (needsDateTimeRFC1123Helper) {
content.push(new Content('time_rfc1123', generateRFC1123Helper(preamble, needsSerDeHelpers)));
}
if (needsDateTimeRFC3339Helper || needsTimeRFC3339Helper) {
content.push(new Content('time_rfc3339', generateRFC3339Helper(preamble, needsDateTimeRFC3339Helper, needsTimeRFC3339Helper, needsSerDeHelpers)));
}
if (needsUnixTimeHelper) {
content.push(new Content('time_unix', generateUnixTimeHelper(preamble, needsSerDeHelpers)));
}
if (needsDateHelper) {
content.push(new Content('date_type', generateDateHelper(preamble, needsSerDeHelpers)));
}
return content;
}
function generateRFC1123Helper(preamble, needsPopulate) {
const imports = new ImportManager();
imports.add('strings');
imports.add('time');
if (needsPopulate) {
imports.add('encoding/json');
imports.add('fmt');
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore');
imports.add('reflect');
}
let text = `${preamble}
${imports.text()}
const (
dateTimeRFC1123JSON = \`"\` + time.RFC1123 + \`"\`
)
type dateTimeRFC1123 time.Time
func (t dateTimeRFC1123) MarshalJSON() ([]byte, error) {
b := []byte(time.Time(t).Format(dateTimeRFC1123JSON))
return b, nil
}
func (t dateTimeRFC1123) MarshalText() ([]byte, error) {
b := []byte(time.Time(t).Format(time.RFC1123))
return b, nil
}
func (t *dateTimeRFC1123) UnmarshalJSON(data []byte) error {
p, err := time.Parse(dateTimeRFC1123JSON, strings.ToUpper(string(data)))
*t = dateTimeRFC1123(p)
return err
}
func (t *dateTimeRFC1123) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
p, err := time.Parse(time.RFC1123, string(data))
*t = dateTimeRFC1123(p)
return err
}
func (t dateTimeRFC1123) String() string {
return time.Time(t).Format(time.RFC1123)
}
`;
if (needsPopulate) {
text +=
`
func populateDateTimeRFC1123(m map[string]any, k string, t *time.Time) {
if t == nil {
return
} else if azcore.IsNullValue(t) {
m[k] = nil
return
} else if reflect.ValueOf(t).IsNil() {
return
}
m[k] = (*dateTimeRFC1123)(t)
}
func unpopulateDateTimeRFC1123(data json.RawMessage, fn string, t **time.Time) error {
if data == nil || string(data) == "null" {
return nil
}
var aux dateTimeRFC1123
if err := json.Unmarshal(data, &aux); err != nil {
return fmt.Errorf("struct field %s: %v", fn, err)
}
*t = (*time.Time)(&aux)
return nil
}
`;
}
return text;
}
function generateRFC3339Helper(preamble, dateTime, time, needsPopulate) {
const imports = new ImportManager();
imports.add('regexp');
imports.add('strings');
imports.add('time');
if (needsPopulate) {
imports.add('encoding/json');
imports.add('fmt');
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore');
imports.add('reflect');
}
let text = `${preamble}
${imports.text()}
// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases.
var tzOffsetRegex = regexp.MustCompile(\`(?:Z|z|\\+|-)(?:\\d+:\\d+)*"*$\`)
`;
if (dateTime) {
text +=
`
const (
utcDateTime = "2006-01-02T15:04:05.999999999"
utcDateTimeJSON = \`"\` + utcDateTime + \`"\`
utcDateTimeNoT = "2006-01-02 15:04:05.999999999"
utcDateTimeJSONNoT = \`"\` + utcDateTimeNoT + \`"\`
dateTimeNoT = \`2006-01-02 15:04:05.999999999Z07:00\`
dateTimeJSON = \`"\` + time.RFC3339Nano + \`"\`
dateTimeJSONNoT = \`"\` + dateTimeNoT + \`"\`
)
type dateTimeRFC3339 time.Time
func (t dateTimeRFC3339) MarshalJSON() ([]byte, error) {
tt := time.Time(t)
return tt.MarshalJSON()
}
func (t dateTimeRFC3339) MarshalText() ([]byte, error) {
tt := time.Time(t)
return tt.MarshalText()
}
func (t *dateTimeRFC3339) UnmarshalJSON(data []byte) error {
tzOffset := tzOffsetRegex.Match(data)
hasT := strings.Contains(string(data), "T") || strings.Contains(string(data), "t")
var layout string
if tzOffset && hasT {
layout = dateTimeJSON
} else if tzOffset {
layout = dateTimeJSONNoT
} else if hasT {
layout = utcDateTimeJSON
} else {
layout = utcDateTimeJSONNoT
}
return t.Parse(layout, string(data))
}
func (t *dateTimeRFC3339) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
tzOffset := tzOffsetRegex.Match(data)
hasT := strings.Contains(string(data), "T") || strings.Contains(string(data), "t")
var layout string
if tzOffset && hasT {
layout = time.RFC3339Nano
} else if tzOffset {
layout = dateTimeNoT
} else if hasT {
layout = utcDateTime
} else {
layout = utcDateTimeNoT
}
return t.Parse(layout, string(data))
}
func (t *dateTimeRFC3339) Parse(layout, value string) error {
p, err := time.Parse(layout, strings.ToUpper(value))
*t = dateTimeRFC3339(p)
return err
}
func (t dateTimeRFC3339) String() string {
return time.Time(t).Format(time.RFC3339Nano)
}
`;
if (needsPopulate) {
text +=
`
func populateDateTimeRFC3339(m map[string]any, k string, t *time.Time) {
if t == nil {
return
} else if azcore.IsNullValue(t) {
m[k] = nil
return
} else if reflect.ValueOf(t).IsNil() {
return
}
m[k] = (*dateTimeRFC3339)(t)
}
func unpopulateDateTimeRFC3339(data json.RawMessage, fn string, t **time.Time) error {
if data == nil || string(data) == "null" {
return nil
}
var aux dateTimeRFC3339
if err := json.Unmarshal(data, &aux); err != nil {
return fmt.Errorf("struct field %s: %v", fn, err)
}
*t = (*time.Time)(&aux)
return nil
}
`;
}
}
if (time) {
text += `
const (
utcTimeJSON = \`"15:04:05.999999999"\`
utcTime = "15:04:05.999999999"
timeFormat = "15:04:05.999999999Z07:00"
)
type timeRFC3339 time.Time
func (t timeRFC3339) MarshalJSON() ([]byte, error) {
s, _ := t.MarshalText()
return []byte(fmt.Sprintf("\\"%s\\"", s)), nil
}
func (t timeRFC3339) MarshalText() ([]byte, error) {
tt := time.Time(t)
return []byte(tt.Format(timeFormat)), nil
}
func (t *timeRFC3339) UnmarshalJSON(data []byte) error {
layout := utcTimeJSON
if tzOffsetRegex.Match(data) {
layout = timeFormat
}
return t.Parse(layout, string(data))
}
func (t *timeRFC3339) UnmarshalText(data []byte) error {
if len(data) == 0 {
return nil
}
layout := utcTime
if tzOffsetRegex.Match(data) {
layout = timeFormat
}
return t.Parse(layout, string(data))
}
func (t *timeRFC3339) Parse(layout, value string) error {
p, err := time.Parse(layout, strings.ToUpper(value))
*t = timeRFC3339(p)
return err
}
func (t timeRFC3339) String() string {
tt := time.Time(t)
return tt.Format(timeFormat)
}
`;
if (needsPopulate) {
text += `
func populateTimeRFC3339(m map[string]any, k string, t *time.Time) {
if t == nil {
return
} else if azcore.IsNullValue(t) {
m[k] = nil
return
} else if reflect.ValueOf(t).IsNil() {
return
}
m[k] = (*timeRFC3339)(t)
}
func unpopulateTimeRFC3339(data json.RawMessage, fn string, t **time.Time) error {
if data == nil || string(data) == "null" {
return nil
}
var aux timeRFC3339
if err := json.Unmarshal(data, &aux); err != nil {
return fmt.Errorf("struct field %s: %v", fn, err)
}
*t = (*time.Time)(&aux)
return nil
}
`;
}
}
return text;
}
function generateUnixTimeHelper(preamble, needsPopulate) {
const imports = new ImportManager();
imports.add('encoding/json');
imports.add('fmt');
imports.add('time');
if (needsPopulate) {
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore');
imports.add('reflect');
}
let text = `${preamble}
${imports.text()}
type timeUnix time.Time
func (t timeUnix) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(t).Unix())
}
func (t *timeUnix) UnmarshalJSON(data []byte) error {
var seconds int64
if err := json.Unmarshal(data, &seconds); err != nil {
return err
}
*t = timeUnix(time.Unix(seconds, 0))
return nil
}
func (t timeUnix) String() string {
return fmt.Sprintf("%d", time.Time(t).Unix())
}
`;
if (needsPopulate) {
text +=
`
func populateTimeUnix(m map[string]any, k string, t *time.Time) {
if t == nil {
return
} else if azcore.IsNullValue(t) {
m[k] = nil
return
} else if reflect.ValueOf(t).IsNil() {
return
}
m[k] = (*timeUnix)(t)
}
func unpopulateTimeUnix(data json.RawMessage, fn string, t **time.Time) error {
if data == nil || string(data) == "null" {
return nil
}
var aux timeUnix
if err := json.Unmarshal(data, &aux); err != nil {
return fmt.Errorf("struct field %s: %v", fn, err)
}
*t = (*time.Time)(&aux)
return nil
}
`;
}
return text;
}
function generateDateHelper(preamble, needsPopulate) {
const imports = new ImportManager();
imports.add('fmt');
imports.add('time');
if (needsPopulate) {
imports.add('encoding/json');
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore');
imports.add('reflect');
}
let text = `${preamble}
${imports.text()}
const (
fullDateJSON = \`"2006-01-02"\`
jsonFormat = \`"%04d-%02d-%02d"\`
)
type dateType time.Time
func (t dateType) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(jsonFormat, time.Time(t).Year(), time.Time(t).Month(), time.Time(t).Day())), nil
}
func (d *dateType) UnmarshalJSON(data []byte) (err error) {
t, err := time.Parse(fullDateJSON, string(data))
*d = (dateType)(t)
return err
}
`;
if (needsPopulate) {
text +=
`
func populateDateType(m map[string]any, k string, t *time.Time) {
if t == nil {
return
} else if azcore.IsNullValue(t) {
m[k] = nil
return
} else if reflect.ValueOf(t).IsNil() {
return
}
m[k] = (*dateType)(t)
}
func unpopulateDateType(data json.RawMessage, fn string, t **time.Time) error {
if data == nil || string(data) == "null" {
return nil
}
var aux dateType
if err := json.Unmarshal(data, &aux); err != nil {
return fmt.Errorf("struct field %s: %v", fn, err)
}
*t = (*time.Time)(&aux)
return nil
}
`;
}
return text;
}
//# sourceMappingURL=time.js.map