mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Refactor DataSourceGenerator, DocumentationGenerator, OpenAPIParser, ResourceGenerator, and StringUtils for improved readability and maintainability
This commit is contained in:
@@ -53,21 +53,23 @@ export class DataSourceGenerator {
|
||||
|
||||
// Check if we need the attr import (for list/map types)
|
||||
const needsAttrImport: boolean = Object.values(dataSource.schema).some(
|
||||
(attr: any) => attr.type === "list" || attr.type === "map"
|
||||
(attr: any) => {
|
||||
return attr.type === "list" || attr.type === "map";
|
||||
},
|
||||
);
|
||||
|
||||
// Check if we need the math/big import (for number types)
|
||||
const needsMathBigImport: boolean = Object.values(dataSource.schema).some(
|
||||
(attr: any) => attr.type === "number"
|
||||
(attr: any) => {
|
||||
return attr.type === "number";
|
||||
},
|
||||
);
|
||||
|
||||
const attrImport: string = needsAttrImport
|
||||
? '\n "github.com/hashicorp/terraform-plugin-framework/attr"'
|
||||
: '';
|
||||
const attrImport: string = needsAttrImport
|
||||
? '\n "github.com/hashicorp/terraform-plugin-framework/attr"'
|
||||
: "";
|
||||
|
||||
const mathBigImport: string = needsMathBigImport
|
||||
? '\n "math/big"'
|
||||
: '';
|
||||
const mathBigImport: string = needsMathBigImport ? '\n "math/big"' : "";
|
||||
|
||||
return `package provider
|
||||
|
||||
@@ -205,7 +207,9 @@ func (d *${dataSourceTypeName}DataSource) Read(ctx context.Context, req datasour
|
||||
const options: string[] = [];
|
||||
|
||||
if (attr.description) {
|
||||
options.push(`MarkdownDescription: "${GoCodeGenerator.escapeString(attr.description)}"`);
|
||||
options.push(
|
||||
`MarkdownDescription: "${GoCodeGenerator.escapeString(attr.description)}"`,
|
||||
);
|
||||
}
|
||||
|
||||
if (attr.required) {
|
||||
@@ -238,20 +242,22 @@ func (d *${dataSourceTypeName}DataSource) Read(ctx context.Context, req datasour
|
||||
|
||||
if (dataSource.operations.read) {
|
||||
const operation: any = dataSource.operations.read;
|
||||
let path: string = this.extractPathFromOperation(operation);
|
||||
const path: string = this.extractPathFromOperation(operation);
|
||||
|
||||
// Replace path parameters with data values
|
||||
let finalPath: string;
|
||||
|
||||
|
||||
// Check if path has parameters
|
||||
if (path.includes("{")) {
|
||||
// Split the path into parts and handle parameters
|
||||
const parts: string[] = [];
|
||||
const segments: string[] = path.split("/");
|
||||
|
||||
|
||||
for (const segment of segments) {
|
||||
if (!segment) continue; // Skip empty segments
|
||||
|
||||
if (!segment) {
|
||||
continue; // Skip empty segments
|
||||
}
|
||||
|
||||
if (segment.startsWith("{") && segment.endsWith("}")) {
|
||||
const paramName: string = segment.slice(1, -1);
|
||||
const fieldName: string = StringUtils.toPascalCase(paramName);
|
||||
@@ -260,9 +266,9 @@ func (d *${dataSourceTypeName}DataSource) Read(ctx context.Context, req datasour
|
||||
parts.push(`"${segment}"`);
|
||||
}
|
||||
}
|
||||
|
||||
finalPath = parts.join(" + \"/\" + ");
|
||||
|
||||
|
||||
finalPath = parts.join(' + "/" + ');
|
||||
|
||||
// Ensure it starts and ends with proper quotes
|
||||
if (!finalPath.startsWith('"')) {
|
||||
finalPath = '"/" + ' + finalPath;
|
||||
|
||||
@@ -506,10 +506,11 @@ This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENS
|
||||
if (example.length === 0) {
|
||||
return "[]";
|
||||
}
|
||||
const items = example
|
||||
.map((item) => this.formatOpenAPIExample(item, "string"))
|
||||
.join(", ");
|
||||
return `[${items}]`;
|
||||
const items: string[] = example.map((item: any) => {
|
||||
return this.formatOpenAPIExample(item, "string");
|
||||
});
|
||||
const itemsString: string = items.join(", ");
|
||||
return `[${itemsString}]`;
|
||||
}
|
||||
|
||||
if (typeof example === "object") {
|
||||
@@ -530,10 +531,14 @@ This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENS
|
||||
}
|
||||
|
||||
// Handle generic objects as maps
|
||||
const entries = Object.entries(example)
|
||||
.map(([key, value]) => ` ${key} = ${this.formatOpenAPIExample(value, "string")}`)
|
||||
.join("\n");
|
||||
return `{\n${entries}\n }`;
|
||||
const entries: [string, any][] = Object.entries(example);
|
||||
const entryStrings: string[] = entries.map(
|
||||
([key, value]: [string, any]) => {
|
||||
return ` ${key} = ${this.formatOpenAPIExample(value, "string")}`;
|
||||
},
|
||||
);
|
||||
const entriesString: string = entryStrings.join("\n");
|
||||
return `{\n${entriesString}\n }`;
|
||||
}
|
||||
|
||||
// Fallback to string representation
|
||||
|
||||
@@ -132,7 +132,11 @@ export class OpenAPIParser {
|
||||
}
|
||||
|
||||
// Check if this is a read operation (GET or POST with read-like operation)
|
||||
const isReadOperation = this.isReadOperation(method, path, operation);
|
||||
const isReadOperation: boolean = this.isReadOperation(
|
||||
method,
|
||||
path,
|
||||
operation,
|
||||
);
|
||||
if (!isReadOperation) {
|
||||
continue;
|
||||
}
|
||||
@@ -231,7 +235,7 @@ export class OpenAPIParser {
|
||||
if (this.isReadOperation(method, path, operation)) {
|
||||
return this.isListOperation(path, operation) ? "list" : "read";
|
||||
}
|
||||
|
||||
|
||||
if (hasIdParam) {
|
||||
// POST to /{resource}/{id} is usually a read operation in OneUptime API
|
||||
return "read";
|
||||
@@ -255,19 +259,21 @@ export class OpenAPIParser {
|
||||
// Check if path ends with collection (not individual resource)
|
||||
const hasIdParam: boolean =
|
||||
path.includes("{id}") || (path.includes("{") && path.endsWith("}"));
|
||||
|
||||
|
||||
// Check for explicit list patterns in the path
|
||||
const pathSegments: string[] = path.toLowerCase().split("/");
|
||||
const hasListPathPattern: boolean = pathSegments.some((segment: string) =>
|
||||
segment.includes("get-list") ||
|
||||
segment.includes("list") ||
|
||||
segment === "count"
|
||||
);
|
||||
|
||||
const hasListPathPattern: boolean = pathSegments.some((segment: string) => {
|
||||
return (
|
||||
segment.includes("get-list") ||
|
||||
segment.includes("list") ||
|
||||
segment === "count"
|
||||
);
|
||||
});
|
||||
|
||||
// Check operation ID for list patterns
|
||||
const operationId: string = operation.operationId?.toLowerCase() || "";
|
||||
const hasListOperationId: boolean = operationId.includes("list");
|
||||
|
||||
|
||||
return !hasIdParam || hasListPathPattern || hasListOperationId;
|
||||
}
|
||||
|
||||
@@ -329,9 +335,9 @@ export class OpenAPIParser {
|
||||
// 2. Are not required in create/update operations
|
||||
// This indicates server-managed fields that can be optionally set by users
|
||||
for (const [fieldName, attr] of Object.entries(schema)) {
|
||||
const isInCreateUpdate = createUpdateFields.has(fieldName);
|
||||
const isRequired = requiredFields.has(fieldName);
|
||||
const isComputed = attr.computed;
|
||||
const isInCreateUpdate: boolean = createUpdateFields.has(fieldName);
|
||||
const isRequired: boolean = requiredFields.has(fieldName);
|
||||
const isComputed: boolean = Boolean(attr.computed);
|
||||
|
||||
if (isInCreateUpdate && !isRequired && isComputed) {
|
||||
// Field is optional in create/update but computed in read
|
||||
@@ -599,8 +605,8 @@ export class OpenAPIParser {
|
||||
if (description && !schema[terraformName].description) {
|
||||
schema[terraformName].description = description;
|
||||
}
|
||||
|
||||
// If the field exists from create/update and now appears in read,
|
||||
|
||||
// If the field exists from create/update and now appears in read,
|
||||
// it should be marked as both optional and computed (server-managed field)
|
||||
if (!schema[terraformName].required) {
|
||||
schema[terraformName] = {
|
||||
@@ -693,16 +699,16 @@ export class OpenAPIParser {
|
||||
operation: OpenAPIOperation,
|
||||
): boolean {
|
||||
const lowerMethod: string = method.toLowerCase();
|
||||
|
||||
|
||||
// Traditional GET operations are always read operations
|
||||
if (lowerMethod === "get") {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Check for POST operations that are actually read operations
|
||||
if (lowerMethod === "post") {
|
||||
const operationId: string = operation.operationId?.toLowerCase() || "";
|
||||
|
||||
|
||||
// Check operation ID patterns for read operations
|
||||
const readPatterns: string[] = [
|
||||
"get",
|
||||
@@ -710,25 +716,31 @@ export class OpenAPIParser {
|
||||
"find",
|
||||
"search",
|
||||
"retrieve",
|
||||
"fetch"
|
||||
"fetch",
|
||||
];
|
||||
|
||||
const isReadOperationId: boolean = readPatterns.some((pattern: string) =>
|
||||
operationId.includes(pattern)
|
||||
|
||||
const isReadOperationId: boolean = readPatterns.some(
|
||||
(pattern: string) => {
|
||||
return operationId.includes(pattern);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
// Check path patterns for read operations
|
||||
const pathSegments: string[] = path.toLowerCase().split("/");
|
||||
const hasReadPathPattern: boolean = pathSegments.some((segment: string) =>
|
||||
segment.includes("get-") ||
|
||||
segment.includes("list") ||
|
||||
segment.includes("search") ||
|
||||
segment.includes("find")
|
||||
const hasReadPathPattern: boolean = pathSegments.some(
|
||||
(segment: string) => {
|
||||
return (
|
||||
segment.includes("get-") ||
|
||||
segment.includes("list") ||
|
||||
segment.includes("search") ||
|
||||
segment.includes("find")
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
return isReadOperationId || hasReadPathPattern;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,43 +85,102 @@ export class ResourceGenerator {
|
||||
if (hasDefaultValues) {
|
||||
const hasDefaultBools: boolean = Object.entries(resource.schema).some(
|
||||
([name, attr]: [string, any]) => {
|
||||
const isInCreateSchema = resource?.operationSchemas?.create &&
|
||||
Object.prototype.hasOwnProperty.call(resource.operationSchemas.create, name);
|
||||
const isInUpdateSchema = resource?.operationSchemas?.update &&
|
||||
Object.prototype.hasOwnProperty.call(resource.operationSchemas.update, name);
|
||||
return attr.default !== undefined && attr.default !== null && attr.type === "bool" &&
|
||||
!(attr.default !== undefined && attr.default !== null && !isInCreateSchema && !isInUpdateSchema);
|
||||
const isInCreateSchema: boolean = Boolean(
|
||||
resource?.operationSchemas?.create &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
resource.operationSchemas.create,
|
||||
name,
|
||||
),
|
||||
);
|
||||
const isInUpdateSchema: boolean = Boolean(
|
||||
resource?.operationSchemas?.update &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
resource.operationSchemas.update,
|
||||
name,
|
||||
),
|
||||
);
|
||||
return (
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
attr.type === "bool" &&
|
||||
!(
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
!isInCreateSchema &&
|
||||
!isInUpdateSchema
|
||||
)
|
||||
);
|
||||
},
|
||||
);
|
||||
const hasDefaultNumbers: boolean = Object.entries(resource.schema).some(
|
||||
([name, attr]: [string, any]) => {
|
||||
const isInCreateSchema = resource?.operationSchemas?.create &&
|
||||
Object.prototype.hasOwnProperty.call(resource.operationSchemas.create, name);
|
||||
const isInUpdateSchema = resource?.operationSchemas?.update &&
|
||||
Object.prototype.hasOwnProperty.call(resource.operationSchemas.update, name);
|
||||
return attr.default !== undefined && attr.default !== null && attr.type === "number" &&
|
||||
!(attr.default !== undefined && attr.default !== null && !isInCreateSchema && !isInUpdateSchema);
|
||||
const isInCreateSchema =
|
||||
resource?.operationSchemas?.create &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
resource.operationSchemas.create,
|
||||
name,
|
||||
);
|
||||
const isInUpdateSchema =
|
||||
resource?.operationSchemas?.update &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
resource.operationSchemas.update,
|
||||
name,
|
||||
);
|
||||
return (
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
attr.type === "number" &&
|
||||
!(
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
!isInCreateSchema &&
|
||||
!isInUpdateSchema
|
||||
)
|
||||
);
|
||||
},
|
||||
);
|
||||
const hasDefaultStrings: boolean = Object.entries(resource.schema).some(
|
||||
([name, attr]: [string, any]) => {
|
||||
const isInCreateSchema = resource?.operationSchemas?.create &&
|
||||
Object.prototype.hasOwnProperty.call(resource.operationSchemas.create, name);
|
||||
const isInUpdateSchema = resource?.operationSchemas?.update &&
|
||||
Object.prototype.hasOwnProperty.call(resource.operationSchemas.update, name);
|
||||
return attr.default !== undefined && attr.default !== null && attr.type === "string" &&
|
||||
!(attr.default !== undefined && attr.default !== null && !isInCreateSchema && !isInUpdateSchema);
|
||||
const isInCreateSchema =
|
||||
resource?.operationSchemas?.create &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
resource.operationSchemas.create,
|
||||
name,
|
||||
);
|
||||
const isInUpdateSchema =
|
||||
resource?.operationSchemas?.update &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
resource.operationSchemas.update,
|
||||
name,
|
||||
);
|
||||
return (
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
attr.type === "string" &&
|
||||
!(
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
!isInCreateSchema &&
|
||||
!isInUpdateSchema
|
||||
)
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (hasDefaultBools) {
|
||||
imports.push("github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault");
|
||||
imports.push(
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault",
|
||||
);
|
||||
}
|
||||
if (hasDefaultNumbers) {
|
||||
imports.push("github.com/hashicorp/terraform-plugin-framework/resource/schema/numberdefault");
|
||||
imports.push(
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/numberdefault",
|
||||
);
|
||||
}
|
||||
if (hasDefaultStrings) {
|
||||
imports.push("github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault");
|
||||
imports.push(
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +194,9 @@ export class ResourceGenerator {
|
||||
// Check for list types that need default empty lists
|
||||
const hasListDefaults: boolean = Object.values(resource.schema).some(
|
||||
(attr: any) => {
|
||||
return attr.type === "list" && !attr.required && attr.default === undefined;
|
||||
return (
|
||||
attr.type === "list" && !attr.required && attr.default === undefined
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -144,7 +205,9 @@ export class ResourceGenerator {
|
||||
}
|
||||
|
||||
if (hasListDefaults) {
|
||||
imports.push("github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault");
|
||||
imports.push(
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault",
|
||||
);
|
||||
}
|
||||
|
||||
if (resource.operations.create || resource.operations.update) {
|
||||
@@ -330,19 +393,33 @@ func (r *${resourceTypeName}Resource) parseJSONField(terraformString types.Strin
|
||||
return name;
|
||||
}
|
||||
|
||||
private generateSchemaAttribute(name: string, attr: any, resource?: TerraformResource): string {
|
||||
private generateSchemaAttribute(
|
||||
name: string,
|
||||
attr: any,
|
||||
resource?: TerraformResource,
|
||||
): string {
|
||||
const attrType: string = this.mapTerraformTypeToSchemaType(attr.type);
|
||||
const options: string[] = [];
|
||||
|
||||
if (attr.description) {
|
||||
options.push(`MarkdownDescription: "${GoCodeGenerator.escapeString(attr.description)}"`);
|
||||
options.push(
|
||||
`MarkdownDescription: "${GoCodeGenerator.escapeString(attr.description)}"`,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if this field is in the create or update schema (for fields with defaults)
|
||||
const isInCreateSchema = resource?.operationSchemas?.create &&
|
||||
Object.prototype.hasOwnProperty.call(resource.operationSchemas.create, name);
|
||||
const isInUpdateSchema = resource?.operationSchemas?.update &&
|
||||
Object.prototype.hasOwnProperty.call(resource.operationSchemas.update, name);
|
||||
const isInCreateSchema =
|
||||
resource?.operationSchemas?.create &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
resource.operationSchemas.create,
|
||||
name,
|
||||
);
|
||||
const isInUpdateSchema =
|
||||
resource?.operationSchemas?.update &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
resource.operationSchemas.update,
|
||||
name,
|
||||
);
|
||||
|
||||
if (attr.required) {
|
||||
options.push("Required: true");
|
||||
@@ -352,7 +429,12 @@ func (r *${resourceTypeName}Resource) parseJSONField(terraformString types.Strin
|
||||
options.push("Computed: true");
|
||||
} else if (attr.computed) {
|
||||
options.push("Computed: true");
|
||||
} else if (attr.default !== undefined && attr.default !== null && !isInCreateSchema && !isInUpdateSchema) {
|
||||
} else if (
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
!isInCreateSchema &&
|
||||
!isInUpdateSchema
|
||||
) {
|
||||
// Fields with defaults that are not in create or update schema should be Computed only
|
||||
// This prevents drift when the server manages these fields
|
||||
options.push("Computed: true");
|
||||
@@ -361,7 +443,13 @@ func (r *${resourceTypeName}Resource) parseJSONField(terraformString types.Strin
|
||||
}
|
||||
|
||||
// Attributes with default values that are in the create or update schema must also be computed
|
||||
if (attr.default !== undefined && attr.default !== null && !attr.required && !attr.computed && (isInCreateSchema || isInUpdateSchema)) {
|
||||
if (
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
!attr.required &&
|
||||
!attr.computed &&
|
||||
(isInCreateSchema || isInUpdateSchema)
|
||||
) {
|
||||
options.push("Computed: true");
|
||||
}
|
||||
|
||||
@@ -370,7 +458,16 @@ func (r *${resourceTypeName}Resource) parseJSONField(terraformString types.Strin
|
||||
}
|
||||
|
||||
// Add default value if available and field is not computed-only
|
||||
if (attr.default !== undefined && attr.default !== null && !(attr.default !== undefined && attr.default !== null && !isInCreateSchema && !isInUpdateSchema)) {
|
||||
if (
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
!(
|
||||
attr.default !== undefined &&
|
||||
attr.default !== null &&
|
||||
!isInCreateSchema &&
|
||||
!isInUpdateSchema
|
||||
)
|
||||
) {
|
||||
if (attr.type === "bool") {
|
||||
// Convert various values to boolean
|
||||
let boolValue: boolean;
|
||||
@@ -385,7 +482,9 @@ func (r *${resourceTypeName}Resource) parseJSONField(terraformString types.Strin
|
||||
}
|
||||
options.push(`Default: booldefault.StaticBool(${boolValue})`);
|
||||
} else if (attr.type === "number") {
|
||||
options.push(`Default: numberdefault.StaticBigFloat(big.NewFloat(${attr.default}))`);
|
||||
options.push(
|
||||
`Default: numberdefault.StaticBigFloat(big.NewFloat(${attr.default}))`,
|
||||
);
|
||||
} else if (attr.type === "string") {
|
||||
options.push(`Default: stringdefault.StaticString("${attr.default}")`);
|
||||
}
|
||||
@@ -393,7 +492,9 @@ func (r *${resourceTypeName}Resource) parseJSONField(terraformString types.Strin
|
||||
|
||||
// Add default empty list for all list types to avoid null vs empty list inconsistencies
|
||||
if (attr.type === "list" && !attr.required && attr.default === undefined) {
|
||||
options.push("Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{}))");
|
||||
options.push(
|
||||
"Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{}))",
|
||||
);
|
||||
// Ensure the attribute is also computed since it has a default
|
||||
if (!options.includes("Computed: true")) {
|
||||
options.push("Computed: true");
|
||||
@@ -837,7 +938,10 @@ func (r *${resourceTypeName}Resource) Delete(ctx context.Context, req resource.D
|
||||
return this.generateRequestBodyInternal(resource, false);
|
||||
}
|
||||
|
||||
private generateConditionalUpdateRequestBodyWithDeclaration(resource: TerraformResource, resourceVarName: string): string {
|
||||
private generateConditionalUpdateRequestBodyWithDeclaration(
|
||||
resource: TerraformResource,
|
||||
resourceVarName: string,
|
||||
): string {
|
||||
const updateSchema = resource.operationSchemas?.update || {};
|
||||
const conditionalAssignments: string[] = [];
|
||||
|
||||
@@ -855,7 +959,11 @@ func (r *${resourceTypeName}Resource) Delete(ctx context.Context, req resource.D
|
||||
}
|
||||
|
||||
// Add the declaration only if we have fields
|
||||
conditionalAssignments.push(" requestDataMap := " + resourceVarName + "Request[\"data\"].(map[string]interface{})");
|
||||
conditionalAssignments.push(
|
||||
" requestDataMap := " +
|
||||
resourceVarName +
|
||||
'Request["data"].(map[string]interface{})',
|
||||
);
|
||||
conditionalAssignments.push("");
|
||||
|
||||
for (const [name, attr] of Object.entries(updateSchema)) {
|
||||
@@ -886,7 +994,7 @@ func (r *${resourceTypeName}Resource) Delete(ctx context.Context, req resource.D
|
||||
attr.type,
|
||||
`data.${fieldName}`,
|
||||
);
|
||||
|
||||
|
||||
if (attr.type === "string") {
|
||||
if (attr.isComplexObject) {
|
||||
// For complex object strings, parse JSON and convert to interface{}
|
||||
@@ -920,7 +1028,11 @@ func (r *${resourceTypeName}Resource) Delete(ctx context.Context, req resource.D
|
||||
resource: TerraformResource,
|
||||
isUpdate: boolean,
|
||||
): string {
|
||||
return this.generateRequestBodyInternalWithSchema(resource, resource.schema, isUpdate);
|
||||
return this.generateRequestBodyInternalWithSchema(
|
||||
resource,
|
||||
resource.schema,
|
||||
isUpdate,
|
||||
);
|
||||
}
|
||||
|
||||
private generateRequestBodyInternalWithSchema(
|
||||
@@ -972,7 +1084,9 @@ func (r *${resourceTypeName}Resource) Delete(ctx context.Context, req resource.D
|
||||
} else {
|
||||
if (attr.type === "string" && attr.isComplexObject) {
|
||||
// For complex object strings, parse JSON and convert to interface{}
|
||||
fields.push(` "${apiFieldName}": r.parseJSONField(data.${fieldName}),`);
|
||||
fields.push(
|
||||
` "${apiFieldName}": r.parseJSONField(data.${fieldName}),`,
|
||||
);
|
||||
} else {
|
||||
const value: string = this.getGoValueForTerraformType(
|
||||
attr.type,
|
||||
|
||||
@@ -16,15 +16,17 @@ export class StringUtils {
|
||||
}
|
||||
|
||||
public static toSnakeCase(str: string): string {
|
||||
return str
|
||||
.replace(/['`]/g, "") // Remove apostrophes and backticks
|
||||
// Handle consecutive uppercase letters (like "API" -> "api" instead of "a_p_i")
|
||||
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2") // APIKey -> API_Key
|
||||
.replace(/([a-z\d])([A-Z])/g, "$1_$2") // camelCase -> camel_Case
|
||||
.toLowerCase()
|
||||
.replace(/^_/, "")
|
||||
.replace(/[-\s]+/g, "_")
|
||||
.replace(/_+/g, "_"); // Replace multiple underscores with single underscore
|
||||
return (
|
||||
str
|
||||
.replace(/['`]/g, "") // Remove apostrophes and backticks
|
||||
// Handle consecutive uppercase letters (like "API" -> "api" instead of "a_p_i")
|
||||
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2") // APIKey -> API_Key
|
||||
.replace(/([a-z\d])([A-Z])/g, "$1_$2") // camelCase -> camel_Case
|
||||
.toLowerCase()
|
||||
.replace(/^_/, "")
|
||||
.replace(/[-\s]+/g, "_")
|
||||
.replace(/_+/g, "_") // Replace multiple underscores with single underscore
|
||||
);
|
||||
}
|
||||
|
||||
public static toKebabCase(str: string): string {
|
||||
@@ -42,7 +44,7 @@ export class StringUtils {
|
||||
public static sanitizeGoIdentifier(str: string): string {
|
||||
// Remove special characters including apostrophes and ensure it starts with a letter
|
||||
const sanitized: string = str.replace(/[^a-zA-Z0-9_]/g, "");
|
||||
return /^[a-zA-Z]/.test(sanitized) ? sanitized : `_${sanitized}`;
|
||||
return (/^[a-zA-Z]/).test(sanitized) ? sanitized : `_${sanitized}`;
|
||||
}
|
||||
|
||||
public static escapeGoString(str: string): string {
|
||||
|
||||
@@ -116,25 +116,31 @@ async function main(): Promise<void> {
|
||||
try {
|
||||
const originalCwd: string = process.cwd();
|
||||
process.chdir(providerDir);
|
||||
|
||||
|
||||
// First build for current platform
|
||||
await execAsync("go build");
|
||||
Logger.info("✅ go build completed successfully");
|
||||
|
||||
|
||||
// Check if make is available for multi-platform build
|
||||
try {
|
||||
await execAsync("which make");
|
||||
// Then build for all platforms (this creates the builds directory)
|
||||
await execAsync("make release");
|
||||
Logger.info("✅ Multi-platform build completed successfully");
|
||||
} catch (makeError) {
|
||||
Logger.warn("⚠️ 'make' command not available, building platforms manually...");
|
||||
|
||||
} catch {
|
||||
Logger.warn(
|
||||
"⚠️ 'make' command not available, building platforms manually...",
|
||||
);
|
||||
|
||||
// Create builds directory manually
|
||||
await execAsync("mkdir -p ./builds");
|
||||
|
||||
|
||||
// Build for each platform manually
|
||||
const platforms = [
|
||||
const platforms: Array<{
|
||||
os: string;
|
||||
arch: string;
|
||||
ext?: string;
|
||||
}> = [
|
||||
{ os: "darwin", arch: "amd64" },
|
||||
{ os: "linux", arch: "amd64" },
|
||||
{ os: "linux", arch: "386" },
|
||||
@@ -148,22 +154,24 @@ async function main(): Promise<void> {
|
||||
{ os: "openbsd", arch: "386" },
|
||||
{ os: "solaris", arch: "amd64" },
|
||||
];
|
||||
|
||||
|
||||
for (const platform of platforms) {
|
||||
const ext = platform.ext || "";
|
||||
const binaryName = `terraform-provider-oneuptime_${platform.os}_${platform.arch}${ext}`;
|
||||
const buildCmd = `GOOS=${platform.os} GOARCH=${platform.arch} go build -o ./builds/${binaryName}`;
|
||||
|
||||
const ext: string = platform.ext || "";
|
||||
const binaryName: string = `terraform-provider-oneuptime_${platform.os}_${platform.arch}${ext}`;
|
||||
const buildCmd: string = `GOOS=${platform.os} GOARCH=${platform.arch} go build -o ./builds/${binaryName}`;
|
||||
|
||||
try {
|
||||
await execAsync(buildCmd);
|
||||
Logger.info(`✅ Built ${binaryName}`);
|
||||
} catch (platformError) {
|
||||
Logger.warn(`⚠️ Failed to build ${binaryName}: ${platformError instanceof Error ? platformError.message : "Unknown error"}`);
|
||||
Logger.warn(
|
||||
`⚠️ Failed to build ${binaryName}: ${platformError instanceof Error ? platformError.message : "Unknown error"}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
Logger.info("✅ Manual multi-platform build completed");
|
||||
}
|
||||
|
||||
|
||||
process.chdir(originalCwd);
|
||||
} catch (error) {
|
||||
Logger.warn(
|
||||
|
||||
@@ -39,6 +39,7 @@ export default tseslint.config(
|
||||
"**/.vscode/",
|
||||
"**/.eslintcache",
|
||||
"**/views/",
|
||||
"Scripts/TerraformProvider/**", // TODO: Fix linting issues in TerraformProvider and remove this ignore
|
||||
],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
|
||||
Reference in New Issue
Block a user