Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

When you want to extend your ODC apps with custom C# code, you do so with the [External Libraries SDK](https://success.outsystems.com/documentation/outsystems_developer_cloud/building_apps/extend_your_apps_with_custom_code/external_libraries_sdk_readme/). This SDK allows you to write C# code that you can call from your ODC apps.

Although IntelliSense in your IDE guides the available SDK decorators and their syntax, it does not guide the rules you must follow (for example, on [naming](https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05019) and [design decisions](https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05018)). This guidance is provided when uploading your project's built assembly to the ODC Portal, where you get feedback on rule violations. **Using this component brings that feedback forward and gives you real-time feedback on compliance with the rules as you write the code.**
Although IntelliSense in your IDE guides the available SDK decorators and their syntax, it does not guide the rules you must follow (for example, on [naming](https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05019) and [design decisions](https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05018)). This guidance is provided when uploading your project's built assembly to the ODC Portal, where you get feedback on rule violations. **Using this component brings that feedback forward and gives you real-time feedback on compliance with the rules as you write the code. You also get real-time guidance on best practices.**

### Technical Primer

When you upload your project's built assembly to the ODC Portal, it does not have access to the underlying code—the ODC Portal checks compliance with the rules using the assembly's metadata.
When you upload your project's built assembly to the ODC Portal, it does not have access to the underlying code—the ODC Portal checks compliance with the rules by reflecting on assembly's metadata.

This component, built from scratch, implements the rules using the rich code analysis APIs of [Roslyn](https://github.com/dotnet/roslyn), the .NET compiler.

Expand Down
2 changes: 1 addition & 1 deletion src/CustomCode-Analyzer.Vsix/source.extension.vsixmanifest
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="CustomCode_Analyzer.Vsix.e1c79fe3-d19f-4bf9-9c0c-57ba1f95b494" Version="0.1.2" Language="en-US" Publisher="Jonathan Algar"/>
<Identity Id="CustomCode_Analyzer.Vsix.e1c79fe3-d19f-4bf9-9c0c-57ba1f95b494" Version="0.1.3" Language="en-US" Publisher="Jonathan Algar"/>
<DisplayName>ODC Custom Code Analyzer</DisplayName>
<Description xml:space="preserve">Get feedback on your OutSytems Developer Cloud (ODC) custom C# code as you code.</Description>
<MoreInfo>https://github.com/jonathanalgar/CustomCode-Analyzer</MoreInfo>
Expand Down
26 changes: 24 additions & 2 deletions src/CustomCode-Analyzer/Analyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public static class DiagnosticIds
public const string UnsupportedParameterType = "UnsupportedParameterType";
public const string UnsupportedDefaultValue = "UnsupportedDefaultValue";
public const string PotentialStatefulImplementation = "PotentialStatefulImplementation";
public const string InputSizeLimit = "InputSizeLimit";
}

/// <summary>
Expand Down Expand Up @@ -300,11 +301,21 @@ public static class Categories
title: "Possible stateful behavior",
messageFormat: "The class '{0}' contains static members ({1}) which could persist state between calls. External libraries should be designed to be stateless.",
category: Categories.Design,
defaultSeverity: DiagnosticSeverity.Info,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "External libraries should be designed to be stateless. Consider passing state information as method parameters instead of storing it in fields.",
helpLinkUri: "https://success.outsystems.com/documentation/outsystems_developer_cloud/building_apps/extend_your_apps_with_custom_code/external_libraries_sdk_readme/#architecture");

private static readonly DiagnosticDescriptor InputSizeLimitRule = new(
DiagnosticIds.InputSizeLimit,
title: "Possible input size limit",
messageFormat: "This method accepts binary data. Note that external libraries have a 5.5MB total input size limit. For large files, use a REST API endpoint or file URL instead.",
category: Categories.Design,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "External libraries have a 5.5MB total input size limit. For large binary files, expose them through a REST API endpoint in your app or provide a URL to download them.",
helpLinkUri: "https://success.outsystems.com/documentation/outsystems_developer_cloud/building_apps/extend_your_apps_with_custom_code/external_libraries_sdk_readme/#use-with-large-binary-files");

/// <summary>
/// Returns the full set of DiagnosticDescriptors that this analyzer is capable of producing.
/// </summary>
Expand Down Expand Up @@ -332,7 +343,8 @@ public static class Categories
MissingStructureDecorationRule,
UnsupportedParameterTypeRule,
UnsupportedDefaultValueRule,
PotentialStatefulImplementationRule);
PotentialStatefulImplementationRule,
InputSizeLimitRule);

/// <summary>
/// Entry point for the analyzer. Initializes analysis by setting up compilation-level
Expand Down Expand Up @@ -706,6 +718,16 @@ private static void AnalyzeMethod(SymbolAnalysisContext context, IMethodSymbol m
}
}

// Check for potential input size limit issues
if (parameter.Type is IArrayTypeSymbol arrayType && arrayType.ElementType.SpecialType == SpecialType.System_Byte)
{
context.ReportDiagnostic(
Diagnostic.Create(
InputSizeLimitRule,
methodSyntax.GetLocation()));
break;
}

// Check if the default value is valid (compile-time constant and supported type)
if (parameter.HasExplicitDefaultValue &&
!IsValidParameterDefaultValue(parameter) &&
Expand Down
6 changes: 3 additions & 3 deletions src/CustomCode-Analyzer/CustomCode-Analyzer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
<LangVersion>latest</LangVersion>
<RootNamespace>CustomCode_Analyzer</RootNamespace>
<PackageId>CustomCode.Analyzer</PackageId>
<Version>0.1.2</Version>
<AssemblyVersion>0.1.2</AssemblyVersion>
<FileVersion>0.1.2</FileVersion>
<Version>0.1.3</Version>
<AssemblyVersion>0.1.3</AssemblyVersion>
<FileVersion>0.1.3</FileVersion>
<Authors>Jonathan Algar</Authors>
<Product>OutSystems Developer Cloud (ODC) Custom Code Analyzer</Product>
<Description>Get feedback on your OutSytems Developer Cloud (ODC) custom C# code as you code.</Description>
Expand Down
29 changes: 29 additions & 0 deletions tests/CustomCode-Analyzer.Tests/AnalyzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,35 @@ public int GetTotalCalculations()

await CSharpAnalyzerVerifier<Analyzer>.VerifyAnalyzerAsync(test, TestContext, skipSDKreference: false, expected);
}

// --------------------- InputSizeLimitRule --------------------------------
[TestMethod]
public async Task InputSizeLimitRule_ShowsInfo()
{
var test = @"
namespace TestNamespace
{
[OSInterface]
public interface IDocumentProcessor
{
void ProcessDocument(byte[] documentData);
void ProcessMetadata(string metadata);
}

public class DocumentProcessor : IDocumentProcessor
{
public void ProcessDocument(byte[] documentData) { }
public void ProcessMetadata(string metadata) { }
}
}";
var expected = CSharpAnalyzerVerifier<Analyzer>
.Diagnostic(DiagnosticIds.InputSizeLimit)
.WithSpan(7, 9, 7, 51);

await CSharpAnalyzerVerifier<Analyzer>.VerifyAnalyzerAsync(test, TestContext, skipSDKreference: false, expected);
}
// -------------------------------------------------------------------------

// ----------------------------------------------- MIXED TESTS!
[TestMethod]
public async Task ComplexScenario_MultipleNamingAndStructureIssues_ReportsWarnings()
Expand Down