From 399a2aa155ef9dee35e994e42b095d5c6d623d6e Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Thu, 20 Nov 2025 10:31:41 +0530 Subject: [PATCH 01/12] Add google pubsub artifact to index --- .../src/main/resources/service-index.sqlite | Bin 126976 -> 126976 bytes .../main/resources/trigger_properties.json | 13 ++ .../src/main/resources/service_artifacts.json | 132 ++++++++++++++++++ 3 files changed, 145 insertions(+) diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite b/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite index 26989dc4454021fccbe457195b59d1eee973c963..41ca8b063283c48d83a2779cf4abd85278e14612 100644 GIT binary patch delta 1869 zcmaJ?Z)_7~7=Pcpw%2R#dOOC}b+>NU&N6Xh-JG(mi$d+nQh}{(gMuLJdOf;gcUSJN z$}|LyFMJ>n^b#AANeBU#h#E;FGk(&T_yqyK5JL0=8e_L-f7=!A%!0d7k&~ zx!?QV?|I(e^WO1$#PNH?+152e3INc7tajnwzuxmCCZw*;Tw-w6ea<8r0+{<;xxYbi zZ%wcc&=v6R&5C)YnCUGP(^@f|7V>#X&B?-%{@%XczG|>1t0>vL#8-elbCRYB*_@p1 zsXk}LLiszctI^Le7Xd*|Ngf>Uz>v;VW!XWJP& z-U+89_2rx>P0tr3&P(D0%L7}AvZ&`2IVI?V$AJf6RF)N8utHArVz?W!=FMa%tE<;^I?Vnb{s;M zH7`!2ahAMe63GBY-a%B}BYTV{mHR(t z^9NOBju%#iuVB~9mX#M4?9gAPX1-<~Ycn`h7oTD2qIExVTok!Rza4qV{E0m1C{u+$ zn6*x~7cd7riyn+lQ{Pfa_f@wSQUA+D8D1v|y0}3R#u6rTrqBHO6d7Dz5nfc}gSl)` zt?ZG;R>fmRPPaE5iL6jrq#~~J)l*Y8oO6gKarSA_XGmCp(0nA~~Lo~FbMg7QHkTuuLi^eu&K4jLt)m5!Ev+x$ynzI&}g|V=W$SjN% zx1KAwrF;W)dqe|orvVIw3`o-fUzlA%b~)AQS&5fX>+s4!UPt#8UH(gZysq$tVm{AT zdr{IVx-e&d?0jDJQvh;-7`-9j8)4aksvMF;Jy{)48LZ`3XuF0$uYz;)8&ETEs71`c zH{DBW2Yh2Q{y2`6h#9!mfq#VW#WA!|?_iCo{S@$eC4UI@lM_m+s;H%|3>+S=?t3I| zMN)fTq=3(t^>@*<(+S0j;A0hUF{vk4jhm;x>TIgbu3~m=-$wzCiUCVkh zzGG)%GCo?HvAuFGNxPPa41DOun;myc#BsRZMhrOaBF2{;*zQ#6)=8KmIm5fl13C=N z{Nde9?}}>FKe1d&lkx2n)A34zo4BPLC*h!T(e@>5ShN)^_le~$z=k%;`2cwC()a13 z)D0@)-r>6KI^kl-TjZGY0V42)vt_r@hRs;#22BRLJ{myNgrYt>7E6|nU4s5&d{ILOF|SpRQC<*5r6}vXs7e`0 z)^kE$ON__1?HrAd9@vtIZ>yJTOf1A6L1LAzeXStqvk^WRJjy;}gj|4&`F*Stz?v9V zmrb*%Bi+>ZLDSKXBO45|OU!-vtNq$pXluZC*shd_58zu4`-ts#+Z9v?Xd|~65eFFT z#1bXpI!b)uRrevV_|LCrwZmf67Lq2tAhxB1LO?HuhrWw9!GntRssBb3TLnc6sy$K(UGA|i|DZ6w#KWCr6J8y^kG;-j7$H`lTxpKZ(xR5L7 zEFVe2M4?bD=cJ2@#>igaqBAlSa!n73!I}KcvR$xC+>f<#by(2Rq%+2Ssxy4>qd_=p z2%1Ju`WjoTG0~WuC0s8Gnvtqgx>LNk_ zMXN*|bN&zOrAA9(m#wDd@z0uH&5Y5)c04tl)}twvgncDJYlrauai3Fq{6vER$6Wn2 zsf8o5NXy?5^hXR6p81{5=6LT_U<(S)Fa{;E@dHR`n?78(3d2?Iqe%ZYQZKbKCy2xm zsSO}fBmMHcrE{-yFs#Mem-;k=h`W#gktVxTTBlh!jpe>1F1-SqMpLZ;Cqde-(|Oo~ zfo1Bp??9@jslEr^R9|59tarLjyRa;dXM;|fuc+FwYRadihCD~=x#zG$EZsBQ2j8Ho ozgh(qlPbVNn$8sKlmEN_zS%Yp8|U;7`a_-J1HUzwy@Q|Le=TsA-v9sr diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/trigger_properties.json b/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/trigger_properties.json index 95c103dc32..20723ac0c2 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/trigger_properties.json +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/trigger_properties.json @@ -109,5 +109,18 @@ "messaging", "event" ] + }, + "12": { + "name": "gcloud.pubsub", + "orgName": "ballerinax", + "packageName": "gcloud.pubsub", + "keywords": [ + "gcloud", + "google cloud", + "pub/sub", + "pubsub", + "messaging", + "event" + ] } } diff --git a/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json b/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json index d6599409b4..f7e5c6b5d6 100644 --- a/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json +++ b/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json @@ -1452,6 +1452,138 @@ "kind": "ANNOTATION" } ] + }, + { + "name": "gcloud.pubsub", + "version": "1.0.0", + "serviceTypeSkipList": [], + "serviceDeclaration": { + "displayName": "Google Cloud Pub/Sub Event Integration", + "description": "Create a service to handle messages from a Google Cloud Pub/Sub subscription", + "optionalTypeDescriptor": 1, + "typeDescriptorLabel": "Service Type", + "typeDescriptorDescription": "The name of the service contract type", + "typeDescriptorDefaultValue": "Service", + "addDefaultTypeDescriptor": 0, + "optionalAbsoluteResourcePath": 1, + "absoluteResourcePathLabel": "Service Base Path", + "absoluteResourcePathDescription": "", + "absoluteResourcePathDefaultValue": "", + "optionalStringLiteral": 1, + "stringLiteralLabel": "Service Base Path", + "stringLiteralDescription": "", + "stringLiteralDefaultValue": "", + "listenerKind": "SINGLE_SELECT", + "kind": "event" + }, + "serviceTypes": { + "Service": { + "name": "Service", + "description": "", + "functions": [ + { + "name": "onMessage", + "description": "The `onMessage` remote method will be triggered when a message is received from a Google Cloud Pub/Sub subscription", + "accessor": "", + "kind": "REMOTE", + "returnType": "error?", + "returnError": 1, + "returnTypeEditable": 0, + "enable": 0, + "optional": 0, + "parameters": [ + { + "name": "message", + "label": "Message", + "description": "The received Pub/Sub message", + "kind": "REQUIRED", + "type": "pubsub:Message", + "defaultValue": "", + "importStatements": "", + "nameEditable": 1, + "typeEditable": 1 + }, + { + "name": "caller", + "label": "Caller", + "description": "The Pub/Sub caller to acknowledge the message", + "kind": "OPTIONAL", + "type": "pubsub:Caller", + "defaultValue": "", + "importStatements": "", + "nameEditable": 0, + "typeEditable": 0 + } + ] + }, + { + "name": "onError", + "description": "Triggers when an error occurs during message processing", + "accessor": "", + "kind": "REMOTE", + "returnType": "error?", + "returnError": 1, + "returnTypeEditable": 0, + "enable": 0, + "optional": 1, + "parameters": [ + { + "name": "error", + "label": "Error", + "description": "The error occurred during message processing", + "kind": "REQUIRED", + "type": "pubsub:Error", + "defaultValue": "", + "importStatements": "", + "nameEditable": 1, + "typeEditable": 0 + } + ] + } + ] + } + }, + "annotations": { + "ServiceConfig": { + "attachmentPoints": [ + "SERVICE" + ], + "displayName": "Service Configuration", + "description": "Define advanced subscription configurations", + "typeConstraint": "pubsub:ServiceConfiguration" + } + }, + "initForm": { + "project": { + "label": "Project ID", + "description": "The Google Cloud project ID", + "defaultValue": "", + "placeholder": "\"my-gcp-project\"", + "valueType": "EXPRESSION", + "typeConstraint": "string", + "typeMembers": [], + "sourceKind": "LISTENER_PARAM_REQUIRED", + "selections": [] + }, + "subscription": { + "label": "Subscription", + "description": "The name of the Pub/Sub subscription to pull messages from", + "defaultValue": "", + "placeholder": "", + "valueType": "EXPRESSION", + "typeConstraint": "string", + "typeMembers": [], + "sourceKind": "SOURCE_ANNOTATION", + "selections": [] + } + }, + "readOnlyMetadata": [ + { + "key": "subscriptionId", + "displayName": "Subscription ID", + "kind": "ANNOTATION" + } + ] } ] } From d23d2a797829617e98fc2141d87a2e83d140b140 Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Fri, 21 Nov 2025 11:45:56 +0530 Subject: [PATCH 02/12] Add custom service builder for gc pubsub --- .../builder/ServiceBuilderRouter.java | 7 +- .../service/GcloudPubsubServiceBuilder.java | 102 ++++++++++++++++++ .../extension/util/Constants.java | 2 + .../extension/util/Utils.java | 61 ++++++++--- 4 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/ServiceBuilderRouter.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/ServiceBuilderRouter.java index b28906f3af..75ec5d4ff9 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/ServiceBuilderRouter.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/ServiceBuilderRouter.java @@ -27,6 +27,7 @@ import io.ballerina.servicemodelgenerator.extension.builder.service.AiChatServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.AsbServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.DefaultServiceBuilder; +import io.ballerina.servicemodelgenerator.extension.builder.service.GcloudPubsubServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.GraphqlServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.HttpServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.KafkaServiceBuilder; @@ -57,6 +58,7 @@ import static io.ballerina.servicemodelgenerator.extension.util.Constants.AI; import static io.ballerina.servicemodelgenerator.extension.util.Constants.ASB; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.GCLOUD_PUBSUB; import static io.ballerina.servicemodelgenerator.extension.util.Constants.GRAPHQL; import static io.ballerina.servicemodelgenerator.extension.util.Constants.HTTP; import static io.ballerina.servicemodelgenerator.extension.util.Constants.KAFKA; @@ -66,8 +68,8 @@ import static io.ballerina.servicemodelgenerator.extension.util.Constants.TCP; /** - * ServiceBuilderRouter is responsible for routing service building requests to the appropriate service builder - * based on the protocol type. + * ServiceBuilderRouter is responsible for routing service building requests to the appropriate service builder based on + * the protocol type. * * @since 1.2.0 */ @@ -83,6 +85,7 @@ public class ServiceBuilderRouter { put(KAFKA, KafkaServiceBuilder::new); put(ASB, AsbServiceBuilder::new); put(SOLACE, SolaceServiceBuilder::new); + put(GCLOUD_PUBSUB, GcloudPubsubServiceBuilder::new); }}; public static ServiceNodeBuilder getServiceBuilder(String protocol) { diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java new file mode 100644 index 0000000000..61bb1735cf --- /dev/null +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com) + * + * WSO2 LLC. licenses this file to you 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 io.ballerina.servicemodelgenerator.extension.builder.service; + +import io.ballerina.servicemodelgenerator.extension.model.Function; +import io.ballerina.servicemodelgenerator.extension.model.Service; +import io.ballerina.servicemodelgenerator.extension.model.ServiceInitModel; +import io.ballerina.servicemodelgenerator.extension.model.Value; +import io.ballerina.servicemodelgenerator.extension.model.context.AddServiceInitModelContext; +import io.ballerina.servicemodelgenerator.extension.model.context.ModelFromSourceContext; +import org.eclipse.lsp4j.TextEdit; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static io.ballerina.servicemodelgenerator.extension.util.Constants.CLOSE_BRACE; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.GCLOUD_PUBSUB; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.NEW_LINE; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.ON; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.OPEN_BRACE; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.SERVICE; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.SPACE; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.TWO_NEW_LINES; +import static io.ballerina.servicemodelgenerator.extension.util.DatabindUtil.addDataBindingParam; +import static io.ballerina.servicemodelgenerator.extension.util.JmsUtil.ON_MESSAGE_FUNCTION_NAME; +import static io.ballerina.servicemodelgenerator.extension.util.JmsUtil.buildServiceCodeEdits; +import static io.ballerina.servicemodelgenerator.extension.util.ServiceModelUtils.getRequiredFunctionsForServiceType; +import static io.ballerina.servicemodelgenerator.extension.util.Utils.FunctionAddContext.TRIGGER_ADD; +import static io.ballerina.servicemodelgenerator.extension.util.Utils.buildServiceAnnotation; + +/** + * Builder class for Google Cloud Pub/Sub service. + * + * @since 1.4.0 + */ +public final class GcloudPubsubServiceBuilder extends AbstractServiceBuilder { + + private static final String TYPE_PUBSUB_SERVICE_CONFIG = "pubsub:ServiceConfig"; + private static final String SERVICE_TYPE = "pubsub:Service"; + public static final String PAYLOAD_FIELD_NAME = "data"; + public static final String TYPE_PREFIX = "Message"; + + @Override + public Map> addServiceInitSource(AddServiceInitModelContext context) { + ServiceInitModel serviceInitModel = context.serviceInitModel(); + + ListenerDTO listenerDTO = buildListenerDTO(context); + + String serviceCode = buildPubsubServiceCode(serviceInitModel, listenerDTO); + return buildServiceCodeEdits(context, serviceCode, null); + } + + private String buildPubsubServiceCode(ServiceInitModel serviceInitModel, ListenerDTO listenerDTO) { + Map properties = serviceInitModel.getProperties(); + + String serviceAnnotation = buildServiceAnnotation(TYPE_PUBSUB_SERVICE_CONFIG, properties); + + List functions = getRequiredFunctionsForServiceType(serviceInitModel); + List functionsStr = AbstractServiceBuilder.buildMethodDefinitions( + functions, TRIGGER_ADD, new HashMap<>()); + + return NEW_LINE + + listenerDTO.listenerDeclaration() + + NEW_LINE + + serviceAnnotation + + SERVICE + SPACE + SERVICE_TYPE + SPACE + + ON + SPACE + listenerDTO.listenerVarName() + SPACE + + OPEN_BRACE + + NEW_LINE + + String.join(TWO_NEW_LINES, functionsStr) + NEW_LINE + + CLOSE_BRACE + NEW_LINE; + } + + @Override + public Service getModelFromSource(ModelFromSourceContext context) { + Service service = super.getModelFromSource(context); + addDataBindingParam(service, ON_MESSAGE_FUNCTION_NAME, context, PAYLOAD_FIELD_NAME, TYPE_PREFIX); + return service; + } + + @Override + public String kind() { + return GCLOUD_PUBSUB; + } +} diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/Constants.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/Constants.java index df99e7d2bd..ae03000b85 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/Constants.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/Constants.java @@ -66,6 +66,7 @@ public class Constants { public static final String ASB = "asb"; public static final String SF = "salesforce"; public static final String TRIGGER_GITHUB = "trigger.github"; + public static final String GCLOUD_PUBSUB = "gcloud.pubsub"; public static final String FTP = "ftp"; public static final String FILE = "file"; @@ -125,6 +126,7 @@ public class Constants { "LISTENER_PARAM_INCLUDED_DEFAULTABLE_FIELD"; public static final String ARG_TYPE_SERVICE_BASE_PATH = "SERVICE_BASE_PATH"; public static final String ARG_TYPE_SERVICE_TYPE_DESCRIPTOR = "SERVICE_TYPE_DESCRIPTOR"; + public static final String ARG_TYPE_SERVICE_ANNOTATION = "SOURCE_ANNOTATION"; public static final String TYPE_SERVICE = "Service"; diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/Utils.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/Utils.java index 5a4f1f8e3e..e960dd7066 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/Utils.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/Utils.java @@ -106,10 +106,13 @@ import java.util.stream.Collectors; import static io.ballerina.servicemodelgenerator.extension.util.Constants.ANNOT_PREFIX; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.AT; import static io.ballerina.servicemodelgenerator.extension.util.Constants.BALLERINA; import static io.ballerina.servicemodelgenerator.extension.util.Constants.CD_TYPE_ANNOTATION_ATTACHMENT; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.CLOSE_BRACE; import static io.ballerina.servicemodelgenerator.extension.util.Constants.CLOSE_PAREN; import static io.ballerina.servicemodelgenerator.extension.util.Constants.COLON; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.COMMA; import static io.ballerina.servicemodelgenerator.extension.util.Constants.GET; import static io.ballerina.servicemodelgenerator.extension.util.Constants.GRAPHQL_CONTEXT; import static io.ballerina.servicemodelgenerator.extension.util.Constants.GRAPHQL_FIELD; @@ -122,6 +125,7 @@ import static io.ballerina.servicemodelgenerator.extension.util.Constants.KIND_RESOURCE; import static io.ballerina.servicemodelgenerator.extension.util.Constants.KIND_SUBSCRIPTION; import static io.ballerina.servicemodelgenerator.extension.util.Constants.NEW_LINE; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.OPEN_BRACE; import static io.ballerina.servicemodelgenerator.extension.util.Constants.OPEN_PAREN; import static io.ballerina.servicemodelgenerator.extension.util.Constants.PROPERTY_DESIGN_APPROACH; import static io.ballerina.servicemodelgenerator.extension.util.Constants.REMOTE; @@ -221,8 +225,8 @@ public static void populateDesignApproach(Service service) { } /** - * Applies the properties of the enabled choice from the specified choice property key in the service init model. - * If an enabled choice exists, its properties are added to the service and the choice property is removed. + * Applies the properties of the enabled choice from the specified choice property key in the service init model. If + * an enabled choice exists, its properties are added to the service and the choice property is removed. * * @param service the service initialization model to update * @param key the key of the choice property to process @@ -263,9 +267,9 @@ private static Optional getServiceByServiceType(String serviceType) { * Extracts the line range that encompasses all listener expressions in a service declaration. * * @param serviceNode the service declaration node containing listener expressions - * @return an {@link Optional} containing the {@link LineRange} that spans from the start - * of the first listener expression to the end of the last listener expression, - * or {@link Optional#empty()} if no listener expressions are found + * @return an {@link Optional} containing the {@link LineRange} that spans from the start of the first listener + * expression to the end of the last listener expression, or {@link Optional#empty()} if no listener expressions are + * found */ public static Optional getListenerExpressionsLineRange(ServiceDeclarationNode serviceNode) { SeparatedNodeList expressions = serviceNode.expressions(); @@ -419,7 +423,6 @@ public static Optional getParameterModel(ParameterNode parameterNode) return Optional.empty(); } - private static Parameter createParameter(String paramName, String paramKind, String typeName) { Parameter parameterModel = Parameter.getNewFunctionParameter(); parameterModel.setMetadata(new MetaData(paramName, paramName)); @@ -613,8 +616,8 @@ public static NodeList getParamAnnotations(ParameterNode paramet } /** - * This function will add the annotations of a parameter as properties to the parameter model. - * We can pass a skip list to skip certain annotations. + * This function will add the annotations of a parameter as properties to the parameter model. We can pass a skip + * list to skip certain annotations. * * @param parameterModel Parameter model we need to add the annotations as properties * @param annotations Annotations of the parameter @@ -818,6 +821,42 @@ private static boolean isAnnotationProperty(Value value) { && (value.isEnabledWithValue() || !value.isEnabled() && !value.isEditable()); } + /** + * Builds a generic service annotation string from properties. This method iterates through properties, filters + * those marked as service annotations, and constructs a properly formatted annotation string. + * + * @param annotationType The annotation type identifier (e.g., "pubsub:ServiceConfig") + * @param properties The service properties containing configuration values + * @return The formatted annotation string + */ + public static String buildServiceAnnotation(String annotationType, Map properties) { + StringBuilder annotation = new StringBuilder(); + annotation.append(AT).append(annotationType).append(SPACE).append(OPEN_BRACE).append(NEW_LINE); + + List annotationParams = new ArrayList<>(); + + for (Map.Entry entry : properties.entrySet()) { + String propertyName = entry.getKey(); + Value propertyValue = entry.getValue(); + + if (propertyValue != null && propertyValue.getCodedata() != null) { + String argType = propertyValue.getCodedata().getArgType(); + if (Constants.ARG_TYPE_SERVICE_ANNOTATION.equals(argType)) { + if (propertyValue.getValue() != null && !propertyValue.getValue().isBlank()) { + annotationParams.add(propertyName + COLON + SPACE + propertyValue.getValue()); + } + } + } + } + + if (!annotationParams.isEmpty()) { + annotation.append(String.join(COMMA + NEW_LINE, annotationParams)).append(NEW_LINE); + } + + annotation.append(CLOSE_BRACE).append(NEW_LINE); + return annotation.toString(); + } + public static String getDocumentationEdits(Service service) { String docs = ""; if (Objects.nonNull(service.getDocumentation()) && service.getDocumentation().getValue() != null) { @@ -1077,7 +1116,6 @@ public static String generateFunctionDefSource(Function function, List s hasErrorInReturn = returnParts.contains("error") || returnParts.contains("error?"); } - // function body builder.append("{").append(NEW_LINE); if (hasErrorInReturn) { @@ -1295,9 +1333,8 @@ public static List deserializeSelections(String jsonString) { } /** - * Resolves a Ballerina module by organization, package, and module name. - * If the module is not found locally, attempts to pull it from the central repository, - * notifies the client about the process. + * Resolves a Ballerina module by organization, package, and module name. If the module is not found locally, + * attempts to pull it from the central repository, notifies the client about the process. * * @param orgName the organization name * @param packageName the package name From a28ac483cac5ebb4597e0f114e042fbcd93663d0 Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Fri, 21 Nov 2025 11:47:14 +0530 Subject: [PATCH 03/12] Add databind support for gc pubsub --- .../builder/FunctionBuilderRouter.java | 3 + .../function/GcloudPubsubFunctionBuilder.java | 69 +++++++++++++++++++ .../extension/util/DatabindUtil.java | 58 ++++++++-------- 3 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubsubFunctionBuilder.java diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/FunctionBuilderRouter.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/FunctionBuilderRouter.java index 26352a487b..a57926247a 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/FunctionBuilderRouter.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/FunctionBuilderRouter.java @@ -27,6 +27,7 @@ import io.ballerina.projects.Document; import io.ballerina.projects.Project; import io.ballerina.servicemodelgenerator.extension.builder.function.DefaultFunctionBuilder; +import io.ballerina.servicemodelgenerator.extension.builder.function.GcloudPubsubFunctionBuilder; import io.ballerina.servicemodelgenerator.extension.builder.function.GraphqlFunctionBuilder; import io.ballerina.servicemodelgenerator.extension.builder.function.HttpFunctionBuilder; import io.ballerina.servicemodelgenerator.extension.builder.function.KafkaFunctionBuilder; @@ -50,6 +51,7 @@ import java.util.function.Supplier; import static io.ballerina.servicemodelgenerator.extension.util.Constants.DEFAULT; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.GCLOUD_PUBSUB; import static io.ballerina.servicemodelgenerator.extension.util.Constants.GRAPHQL; import static io.ballerina.servicemodelgenerator.extension.util.Constants.HTTP; import static io.ballerina.servicemodelgenerator.extension.util.Constants.KAFKA; @@ -72,6 +74,7 @@ public class FunctionBuilderRouter { put(MCP, McpFunctionBuilder::new); put(KAFKA, KafkaFunctionBuilder::new); put(SOLACE, SolaceFunctionBuilder::new); + put(GCLOUD_PUBSUB, GcloudPubsubFunctionBuilder::new); }}; private static NodeBuilder getFunctionBuilder(String protocol) { diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubsubFunctionBuilder.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubsubFunctionBuilder.java new file mode 100644 index 0000000000..0b87b30967 --- /dev/null +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubsubFunctionBuilder.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com) + * + * WSO2 LLC. licenses this file to you 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 io.ballerina.servicemodelgenerator.extension.builder.function; + +import io.ballerina.servicemodelgenerator.extension.model.context.AddModelContext; +import io.ballerina.servicemodelgenerator.extension.model.context.UpdateModelContext; +import io.ballerina.servicemodelgenerator.extension.util.DatabindUtil; +import org.eclipse.lsp4j.TextEdit; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static io.ballerina.servicemodelgenerator.extension.builder.service.GcloudPubsubServiceBuilder.PAYLOAD_FIELD_NAME; +import static io.ballerina.servicemodelgenerator.extension.builder.service.GcloudPubsubServiceBuilder.TYPE_PREFIX; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.GCLOUD_PUBSUB; + +/** + * Represents the Google Cloud Pub/Sub function builder of the service model generator. + * + * @since 1.4.0 + */ +public final class GcloudPubsubFunctionBuilder extends AbstractFunctionBuilder { + + private static final String REQUIRED_PARAM_TYPE = "pubsub:Message"; + + @Override + public Map> updateModel(UpdateModelContext context) { + Map> databindEdits = DatabindUtil.processDatabindingUpdate( + context, TYPE_PREFIX, REQUIRED_PARAM_TYPE, PAYLOAD_FIELD_NAME, false); + + Map> mainFileEdits = super.updateModel(context); + Map> allEdits = new HashMap<>(mainFileEdits); + allEdits.putAll(databindEdits); + return allEdits; + } + + @Override + public Map> addModel(AddModelContext context) throws Exception { + Map> databindEdits = DatabindUtil.processDatabindingForAdd( + context, TYPE_PREFIX, REQUIRED_PARAM_TYPE, PAYLOAD_FIELD_NAME, false); + + Map> mainFileEdits = super.addModel(context); + Map> allEdits = new HashMap<>(mainFileEdits); + allEdits.putAll(databindEdits); + return allEdits; + } + + @Override + public String kind() { + return GCLOUD_PUBSUB; + } +} diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/DatabindUtil.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/DatabindUtil.java index 8bf70f1c2e..ed996f89ef 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/DatabindUtil.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/util/DatabindUtil.java @@ -62,7 +62,6 @@ import java.util.Optional; import java.util.Set; -import static io.ballerina.servicemodelgenerator.extension.util.Constants.COLON; import static io.ballerina.servicemodelgenerator.extension.util.Constants.DATA_BINDING; import static io.ballerina.servicemodelgenerator.extension.util.Constants.DATA_BINDING_PROPERTY; import static io.ballerina.servicemodelgenerator.extension.util.Constants.DATA_BINDING_TEMPLATE; @@ -433,7 +432,8 @@ public static Map> processDatabindingForAdd(AddModelConte ); return createTypeDefinitionEdits(context.project(), typeName, baseType, - newDataBindingType, payloadFieldName, context.filePath(), context.workspaceManager()); + context.function().getCodedata().getModuleName(), newDataBindingType, payloadFieldName, + context.filePath(), context.workspaceManager()); } /** @@ -552,7 +552,8 @@ public static Map> processDatabindingUpdate(UpdateModelCo } else { typesEdits = createTypeDefinitionEdits(context.project(), customWrapperTypeName, baseType, - newDataBindingType, payloadFieldName, context.filePath(), context.workspaceManager()); + context.function().getCodedata().getModuleName(), newDataBindingType, payloadFieldName, + context.filePath(), context.workspaceManager()); } } else if (existingTypeName != null) { typeName = existingTypeName; @@ -563,8 +564,9 @@ public static Map> processDatabindingUpdate(UpdateModelCo generateNewDataBindTypeName(context.filePath(), context.workspaceManager(), context.semanticModel(), context.functionNode(), prefix); - typesEdits = createTypeDefinitionEdits(context.project(), typeName, baseType, newDataBindingType, - payloadFieldName, context.filePath(), context.workspaceManager()); + typesEdits = createTypeDefinitionEdits(context.project(), typeName, baseType, + context.function().getCodedata().getModuleName(), newDataBindingType, payloadFieldName, + context.filePath(), context.workspaceManager()); } updateFunctionParameters(function, dataBindingParam, typeName, isArray); @@ -799,24 +801,20 @@ private static String generateNewDataBindTypeName(String contextFilePath, Worksp } /** - * Extracts import statements needed for the given baseType. For example, "kafka:AnydataConsumerRecord" returns - * org/kafka imports. + * Generates the required import statements for the base type if they do not already exist in the module part node. * - * @param baseType The base record type (e.g., "kafka:AnydataConsumerRecord") - * @param modulePartNode The module part node to check existing imports + * @param baseTypeModuleName The module name of the base type + * @param modulePartNode The module part node to check existing imports * @return Set of import statements to add */ - private static Set extractRequiredImports(String baseType, ModulePartNode modulePartNode) { + private static Set extractRequiredImports(String baseTypeModuleName, + ModulePartNode modulePartNode) { Set imports = new HashSet<>(); - if (baseType.contains(COLON)) { - String moduleName = baseType.substring(0, baseType.indexOf(COLON)); - String org = "ballerinax"; - String importModule = moduleName.toLowerCase(java.util.Locale.ENGLISH); + String org = "ballerinax"; - if (!importExists(modulePartNode, org, importModule)) { - imports.add(getImportStmt(org, importModule)); - } + if (!importExists(modulePartNode, org, baseTypeModuleName)) { + imports.add(getImportStmt(org, baseTypeModuleName)); } return imports; @@ -826,13 +824,15 @@ private static Set extractRequiredImports(String baseType, ModulePartNod * Prepares the context for type definition edit operations by loading the types document and extracting required * imports. * - * @param project The Ballerina project - * @param baseType The base record type (e.g., "kafka:AnydataConsumerRecord") - * @param contextFilePath The context file path for locating types.bal - * @param workspaceManager The workspace manager for document retrieval + * @param project The Ballerina project + * @param baseType The base record type (e.g., "kafka:AnydataConsumerRecord") + * @param baseTypeModuleName The module name of the base type + * @param contextFilePath The context file path for locating types.bal + * @param workspaceManager The workspace manager for document retrieval * @return TypeDefinitionEditContext containing types document, module part node, required imports, and project */ private static TypeDefinitionEditContext prepareTypeDefinitionEditContext(Project project, String baseType, + String baseTypeModuleName, String contextFilePath, WorkspaceManager workspaceManager) { Document typesDocument = getTypesDocument(contextFilePath, workspaceManager); @@ -841,7 +841,7 @@ private static TypeDefinitionEditContext prepareTypeDefinitionEditContext(Projec } ModulePartNode modulePartNode = typesDocument.syntaxTree().rootNode(); - Set requiredImports = extractRequiredImports(baseType, modulePartNode); + Set requiredImports = extractRequiredImports(baseTypeModuleName, modulePartNode); return new TypeDefinitionEditContext(typesDocument, modulePartNode, requiredImports, project); } @@ -857,12 +857,14 @@ private static TypeDefinitionEditContext prepareTypeDefinitionEditContext(Projec * @return Map of file paths to TextEdit lists */ private static Map> createTypeDefinitionEdits(Project project, String typeName, - String baseType, String dataBindingType, + String baseType, String baseTypeModuleName, + String dataBindingType, String payloadFieldName, String contextFilePath, WorkspaceManager workspaceManager) { TypeDefinitionEditContext context = - prepareTypeDefinitionEditContext(project, baseType, contextFilePath, workspaceManager); + prepareTypeDefinitionEditContext(project, baseType, baseTypeModuleName, contextFilePath, + workspaceManager); if (context == null) { return Map.of(); } @@ -1101,7 +1103,8 @@ private static Map> updateTypeDefinitionEdits(UpdateModel String newTypeName) { Project project = context.project() != null ? context.project() : context.document().module().project(); TypeDefinitionEditContext editContext = - prepareTypeDefinitionEditContext(project, baseType, context.filePath(), context.workspaceManager()); + prepareTypeDefinitionEditContext(project, baseType, context.function().getCodedata().getModuleName(), + context.filePath(), context.workspaceManager()); if (editContext == null) { return Map.of(); } @@ -1121,8 +1124,9 @@ private static Map> updateTypeDefinitionEdits(UpdateModel if (existingTypeDef == null) { // Type doesn't exist, create it instead - return createTypeDefinitionEdits(context.project(), existingTypeName, baseType, newDataBindingType, - payloadFieldName, context.filePath(), context.workspaceManager()); + return createTypeDefinitionEdits(context.project(), existingTypeName, baseType, + context.function().getCodedata().getModuleName(), newDataBindingType, payloadFieldName, + context.filePath(), context.workspaceManager()); } // Use newTypeName if provided for renaming, otherwise use existingTypeName From 1c2eb4b5aad4bd77004b3c34710d598f9193c1d1 Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Sun, 9 Nov 2025 20:50:23 +0530 Subject: [PATCH 04/12] Add optional and advanced fields to init form props # Conflicts: # service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite # service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json --- .../commons/ServiceDatabaseManager.java | 8 ++++++-- .../commons/ServiceInitProperty.java | 3 ++- .../service/AbstractServiceBuilder.java | 4 +++- .../src/main/resources/service-index.sqlite | Bin 126976 -> 126976 bytes .../indexgenerator/DatabaseManager.java | 9 +++++---- .../indexgenerator/ServiceIndexGenerator.java | 4 ++-- .../src/main/resources/service-index.sql | 2 ++ 7 files changed, 20 insertions(+), 10 deletions(-) diff --git a/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/ServiceDatabaseManager.java b/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/ServiceDatabaseManager.java index 62df806805..712fe1e2f9 100644 --- a/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/ServiceDatabaseManager.java +++ b/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/ServiceDatabaseManager.java @@ -330,7 +330,9 @@ public Optional getServiceInitInfo(String orgName, String modul sql2.append("sip.value_type as valueType, "); sql2.append("sip.type_constraint as typeConstraint, "); sql2.append("sip.source_kind as sourceKind, "); - sql2.append("sip.selections "); + sql2.append("sip.selections, "); + sql2.append("sip.optional, "); + sql2.append("sip.advanced "); sql2.append("FROM ServiceInitializerProperty sip "); sql2.append("WHERE sip.package_id = ?"); @@ -368,7 +370,9 @@ private ServiceInitProperty getServiceInitProperty(ResultSet rs) throws SQLExcep rs.getString("typeConstraint"), rs.getString("sourceKind"), rs.getString("selections"), - getServiceInitPropertyMemberTypes(initializerId) + getServiceInitPropertyMemberTypes(initializerId), + rs.getBoolean("optional"), + rs.getBoolean("advanced") ); } diff --git a/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/ServiceInitProperty.java b/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/ServiceInitProperty.java index 88ec61bd00..15e8dc2c1b 100644 --- a/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/ServiceInitProperty.java +++ b/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/ServiceInitProperty.java @@ -22,5 +22,6 @@ public record ServiceInitProperty(String keyName, String label, String description, String defaultValue, String placeholder, String valueType, String typeConstraint, String sourceKind, - String selections, List memberTypes) { + String selections, List memberTypes, boolean optional, + boolean advanced) { } diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/AbstractServiceBuilder.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/AbstractServiceBuilder.java index 878038d165..1fec47254b 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/AbstractServiceBuilder.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/AbstractServiceBuilder.java @@ -321,7 +321,9 @@ public ServiceInitModel getServiceInitModel(GetServiceInitModelContext context) .setItems(items) .setTypeMembers(property.memberTypes()) .enabled(true) - .editable(true); + .editable(true) + .optional(property.optional()) + .setAdvanced(property.advanced()); serviceInitModel.addProperty(property.keyName(), builder.build()); } diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite b/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite index 41ca8b063283c48d83a2779cf4abd85278e14612..a7f5e413ac8b6753040b2ae23448b55666a1e99a 100644 GIT binary patch delta 1936 zcmeHH&u`mQ9QRA?)JbFeIj)l?u4`MN4Wg}DwTNkyABxtjtprnH>vkwxnk^yi)+KJ5 zj{VRe7HH)#pq&pX7u3thfddRn?1b1rf(x3)5e3Cp zmewRJlFGn}geS04%}&tV>PD&)TzVv+h_m&j4@=ZziZy+VXD{`bf1X%Te=S(IoR6;W zM}fJQxQ0kFi9Yt)uiVfa{|{F@?-kARWU-K$(uemQNDZdm)CW>~lcTR4(0e-dt}gx1 zM6NuME9tQ4W~pfAO64>9K7=yTliC_1`WHy^C=ts}!+1%Ti z*{QNU|NV%={CI&yA^Ya_CTHo!353E<@aC4#CkjvaUarPH8~nC$wXqa<6v+5j{L_9f zYqRYQqHl~@XH15o4|#8S+tAM_LS0<2_K6m&@D!4aUH$z=v|nUd`8A7qXW}s!+LR`v74MLb;X0|+$1XL?E z6th44!@_4H5HzZ$IL+uFCU#`?ByJA_+0{MEbr~IQ`b(>e;ergLEay4dkO&zti>2~N zRa`_pcHz!*_;gcUAFtQ^`JLmqC;_P&Z$S8HHwoW*_o&zxg1Xug7Q>0gemChScQ1!R zA|^>KiL)k+D50H!KRrl1fk_DMby?mH!P<+P?e@8~7jQvvGp0e~R%Cy-HjbxwAQfd; zQR*4H?j6BJ4#;@h9u8`A%lPzO2A>ZCnMfo#wSG!=-Nbw&kg7J#_0>;tVZDOi4FGAx zlibd8G~G9`=?7AlUzQ{znkGlPbpM!mkR>mnrX!I=G;Jxs(i=bF$p+VSw<#EzZ`a59j%5 zN+|MwgJ^+yi~&?Yq>eDiD1iJB$i5jsW}hGgz;*B-xjGs_1^|8lyCbhoVZ`tui)kgT z&;~8aZP!wrYelDUS%nG}V-GZ-+yB)@4dpT40;~}q)S#Kjg;XmV`rtB+aE;bcPmLZY zyAg!1(#z-{;hV1zt=H&zgkMysi#v E0n8kw4QH?pe3RUuiDrC1fN(mh-SpUwLd5dMC9 z0O~HiL=t8$2bcT8{>7zO@P*8RzPkOPbmvkMwD8K07Xi#rS*o$l2KJopBlBdB@^{MQUzT?SgUc7#QHyNycH~p-;m^x&0o&b#&@g-;Jzh_*7IfXe zPA`}kz;b$s>$M|VyI-WOjfi%3Opu*kJEj?VFiS@bM21lyhNmM^SBB`hZCfZ46;ob0 zON%-p$2)?ttPU-BwsMN?ZorgnSzjWWH-Pbr55H1Xjq>=uPW&^T)vJZAk7B^isC={g zj$!c4$zXQl<>18Q*Yr^q<;f$*)XCBZ;~xg!t*3*<$2oeI;+kzAw(TgPzIu`t38GBf zL4svr4gTsdom3EIvpM2K-}7{}M3s6(jypy=qvFSZEz^(d5P6v#*=%lXtwi5XAu^0N zbltP^TJXu*X?8Tp`|9LPGi&92JNR?$E-fS?*&ZuSM2u92iN)Ix99Vysj>Wmk$@5s)=&#q`(@U{PRoHAm?7s#a*yy1X zfNPFB=s3~WRyGRI64o|kbwN_0#zx}wyuilj-*{`md3Dg5&>FYQ#CtQEg~p%xxk+ys z*K;l3vki@FxK>Bw8qWE9r2BRhBs$YCtA4zN|L``PF~Uo#Oi?&T#~m!w_737i;ow$M z3<^|fM&@ODT^yFYRw*->o)fpjt~qfC)H$)AyW5~Hh*kKD-Mj`OzAtfDhVA=oVNqb8 aGLv3^agbgdr2qXORVv>@aX~EM typeMembers, String sourceKind, - List selections) { + List selections, boolean optional, boolean advanced) { } record ServiceInitializerPropertyMemberType(String type, String packageInfo, String kind) { diff --git a/service-model-generator/modules/service-model-index-generator/src/main/resources/service-index.sql b/service-model-generator/modules/service-model-index-generator/src/main/resources/service-index.sql index 0973c18022..e647de64bc 100644 --- a/service-model-generator/modules/service-model-index-generator/src/main/resources/service-index.sql +++ b/service-model-generator/modules/service-model-index-generator/src/main/resources/service-index.sql @@ -147,6 +147,8 @@ CREATE TABLE ServiceInitializerProperty ( source_kind TEXT CHECK(source_kind IN ('SERVICE_TYPE_DESCRIPTOR', 'SERVICE_BASE_PATH', 'LISTENER_PARAM_REQUIRED', 'LISTENER_PARAM_INCLUDED_DEFAULTABLE_FIELD', 'LISTENER_PARAM_INCLUDED_FIELD', 'SOURCE_ANNOTATION')), selections TEXT, -- Comma-separated values for selection options + optional INTEGER DEFAULT 0, -- Whether the property is optional + advanced INTEGER DEFAULT 0, -- Whether the property is advanced/hidden by default FOREIGN KEY (package_id) REFERENCES Package(package_id) ON DELETE CASCADE ); From d988ff1bf6cd5e1dc213afba1aa57dcfd80b96bf Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Fri, 21 Nov 2025 14:42:44 +0530 Subject: [PATCH 05/12] Add auth credentials file to init form --- .../service/GcloudPubsubServiceBuilder.java | 34 ++++++++++++++++++ .../src/main/resources/service-index.sqlite | Bin 126976 -> 126976 bytes .../src/main/resources/service-index.sql | 2 +- .../src/main/resources/service_artifacts.json | 17 +++++++-- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java index 61bb1735cf..20d9568c78 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java @@ -18,6 +18,7 @@ package io.ballerina.servicemodelgenerator.extension.builder.service; +import io.ballerina.servicemodelgenerator.extension.model.Codedata; import io.ballerina.servicemodelgenerator.extension.model.Function; import io.ballerina.servicemodelgenerator.extension.model.Service; import io.ballerina.servicemodelgenerator.extension.model.ServiceInitModel; @@ -30,6 +31,7 @@ import java.util.List; import java.util.Map; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.ARG_TYPE_LISTENER_PARAM_INCLUDED_FIELD; import static io.ballerina.servicemodelgenerator.extension.util.Constants.CLOSE_BRACE; import static io.ballerina.servicemodelgenerator.extension.util.Constants.GCLOUD_PUBSUB; import static io.ballerina.servicemodelgenerator.extension.util.Constants.NEW_LINE; @@ -38,6 +40,7 @@ import static io.ballerina.servicemodelgenerator.extension.util.Constants.SERVICE; import static io.ballerina.servicemodelgenerator.extension.util.Constants.SPACE; import static io.ballerina.servicemodelgenerator.extension.util.Constants.TWO_NEW_LINES; +import static io.ballerina.servicemodelgenerator.extension.util.Constants.VALUE_TYPE_EXPRESSION; import static io.ballerina.servicemodelgenerator.extension.util.DatabindUtil.addDataBindingParam; import static io.ballerina.servicemodelgenerator.extension.util.JmsUtil.ON_MESSAGE_FUNCTION_NAME; import static io.ballerina.servicemodelgenerator.extension.util.JmsUtil.buildServiceCodeEdits; @@ -54,12 +57,18 @@ public final class GcloudPubsubServiceBuilder extends AbstractServiceBuilder { private static final String TYPE_PUBSUB_SERVICE_CONFIG = "pubsub:ServiceConfig"; private static final String SERVICE_TYPE = "pubsub:Service"; + private static final String PROPERTY_CREDENTIALS = "credentials"; + private static final String PROPERTY_AUTH = "auth"; + private static final String AUTH_CONFIG_TEMPLATE = "{path: \"%s\"}"; public static final String PAYLOAD_FIELD_NAME = "data"; public static final String TYPE_PREFIX = "Message"; @Override public Map> addServiceInitSource(AddServiceInitModelContext context) { ServiceInitModel serviceInitModel = context.serviceInitModel(); + Map properties = serviceInitModel.getProperties(); + + applyCredentialsProperty(properties); ListenerDTO listenerDTO = buildListenerDTO(context); @@ -67,6 +76,31 @@ public Map> addServiceInitSource(AddServiceInitModelConte return buildServiceCodeEdits(context, serviceCode, null); } + private void applyCredentialsProperty(Map properties) { + if (!properties.containsKey(PROPERTY_CREDENTIALS)) { + return; + } + + Value credentialsValue = properties.get(PROPERTY_CREDENTIALS); + if (credentialsValue == null || credentialsValue.getValue() == null + || credentialsValue.getValue().isEmpty()) { + return; + } + + String credentialsPath = credentialsValue.getValue(); + String authConfig = String.format(AUTH_CONFIG_TEMPLATE, credentialsPath); + + Value authValue = new Value.ValueBuilder() + .value(authConfig) + .valueType(VALUE_TYPE_EXPRESSION) + .enabled(true) + .editable(false) + .setCodedata(new Codedata(null, ARG_TYPE_LISTENER_PARAM_INCLUDED_FIELD)) + .build(); + properties.put(PROPERTY_AUTH, authValue); + properties.remove(PROPERTY_CREDENTIALS); + } + private String buildPubsubServiceCode(ServiceInitModel serviceInitModel, ListenerDTO listenerDTO) { Map properties = serviceInitModel.getProperties(); diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite b/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite index a7f5e413ac8b6753040b2ae23448b55666a1e99a..c30e863b34beb58aae78df0783c92123019254c5 100644 GIT binary patch delta 478 zcmZp8z~1nHeS(xw-ggED26-Tc0+zCgIz~)+-!~?(=rhW1PSsy#z$m$yC*cmiid3UC zP?2;aNTqxnkdSZ$62h{qK$4OFH;`nI+ssq&U!PHCGtYuw@$Y5ZQ z1Y#K=7KdUnAT0&NAa8)!azG67Ita@GF-R>)j|dQh)PU3q12IS*Bo5+()osmTjOR6I zRF>ysU{u!4R delta 373 zcmZp8z~1nHeS(xwiZTNOgFFyJ0gKo~9V4a`<&6m}`iyd$Q}ve_FiLIaNw~wWBH1Vn zR3zUBQYjq=B;*`{gs?0tkYwcl4I~*PHuDtx*JqU7%(LK^ywDE@jsiATCO3v3d?p}S zh7S`PZ*P8bAcT?AhL>I3(UGx_d9&l8F2>Eb4y)*J%1HA9)$sGqo6P<}j&a^*MTH7p zH9dYd21ZS7MZx0Iq~heF%z~24{5;Q;U?iS`r_1Iee;t@InE47B`1ARG0x21wNm4*8 z3&i43EC!?{ff(dl5L*I>K|TUuAs`ljVm=@ZatR1?12Gp6a{@63jNY2V7|%O#!m{m! re2kM5m$O`86j;tO0Yo$~3T$V2!1#xM+W|%vAb$f$umB{u04N9m7a2}z diff --git a/service-model-generator/modules/service-model-index-generator/src/main/resources/service-index.sql b/service-model-generator/modules/service-model-index-generator/src/main/resources/service-index.sql index e647de64bc..776203dbc9 100644 --- a/service-model-generator/modules/service-model-index-generator/src/main/resources/service-index.sql +++ b/service-model-generator/modules/service-model-index-generator/src/main/resources/service-index.sql @@ -142,7 +142,7 @@ CREATE TABLE ServiceInitializerProperty ( description TEXT NOT NULL, default_value TEXT, placeholder TEXT, - value_type TEXT CHECK(value_type IN ('TYPE', 'FLAG', 'EXPRESSION', 'SINGLE_SELECT')), + value_type TEXT CHECK(value_type IN ('TYPE', 'FLAG', 'EXPRESSION', 'SINGLE_SELECT', 'FILE_SELECT')), type_constraint TEXT, source_kind TEXT CHECK(source_kind IN ('SERVICE_TYPE_DESCRIPTOR', 'SERVICE_BASE_PATH', 'LISTENER_PARAM_REQUIRED', 'LISTENER_PARAM_INCLUDED_DEFAULTABLE_FIELD', 'LISTENER_PARAM_INCLUDED_FIELD', 'SOURCE_ANNOTATION')), diff --git a/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json b/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json index f7e5c6b5d6..fb9d8e9d35 100644 --- a/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json +++ b/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json @@ -1575,12 +1575,25 @@ "typeMembers": [], "sourceKind": "SOURCE_ANNOTATION", "selections": [] + }, + "credentials": { + "label": "User Credentials", + "description": "The service account credentials JSON file path", + "defaultValue": "", + "placeholder": "\"/path/to/credentials.json\"", + "valueType": "FILE_SELECT", + "typeConstraint": "string", + "optional": true, + "advanced": true, + "typeMembers": [], + "sourceKind": "LISTENER_PARAM_INCLUDED_FIELD", + "selections": [] } }, "readOnlyMetadata": [ { - "key": "subscriptionId", - "displayName": "Subscription ID", + "key": "subscription", + "displayName": "Subscription", "kind": "ANNOTATION" } ] From 911643cc7c86892bcddbaa8ee7e0152d8c055521 Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Fri, 21 Nov 2025 15:36:08 +0530 Subject: [PATCH 06/12] Add gcp integration to artifact mapping --- .../main/java/io/ballerina/artifactsgenerator/Artifact.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/architecture-model-generator/modules/architecture-model-generator-core/src/main/java/io/ballerina/artifactsgenerator/Artifact.java b/architecture-model-generator/modules/architecture-model-generator-core/src/main/java/io/ballerina/artifactsgenerator/Artifact.java index 70db717ed0..764fb1cc5a 100644 --- a/architecture-model-generator/modules/architecture-model-generator-core/src/main/java/io/ballerina/artifactsgenerator/Artifact.java +++ b/architecture-model-generator/modules/architecture-model-generator-core/src/main/java/io/ballerina/artifactsgenerator/Artifact.java @@ -91,7 +91,8 @@ public record Artifact(String id, LineRange location, String type, String name, Map.entry("github", "GitHub Event Integration"), Map.entry("twilio", "Twilio Event Integration"), Map.entry("ai", "AI Agent Services"), - Map.entry("solace", "Solace Event Integration") + Map.entry("solace", "Solace Event Integration"), + Map.entry("gcloud.pubsub", "Google Cloud Pub/Sub Event Integration") ); /** @@ -99,7 +100,8 @@ public record Artifact(String id, LineRange location, String type, String name, * module can have multiple field names to try in order of preference. */ private static final Map moduleAnnotationFields = Map.of( - "solace", new String[]{"queueName", "topicName"} + "solace", new String[]{"queueName", "topicName"}, + "gcloud.pubsub", new String[]{"subscription"} ); public static String getCategory(String type) { From 23df7566a9bc12cfb9b31c867b5a25c7846f043e Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Fri, 21 Nov 2025 15:41:45 +0530 Subject: [PATCH 07/12] Rebuild index after rebase --- .../src/main/resources/service-index.sqlite | Bin 126976 -> 126976 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite b/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite index c30e863b34beb58aae78df0783c92123019254c5..ae6f6d36bd7fc7f47095e85b159e6e8fe946387c 100644 GIT binary patch delta 237 zcmZp8z~1nHeS(xwNg@LSgFFyJ0n4(9Iz~(-i5nAE$1_T7=1I82uOind4V0B>1Sye? z0}@h>KtfoS6-YAj{|1r_lAC!7{_8W!Z{}I>$)8br^Z)lE0+A9x36Rm^P%H+d<$zcQ zh(T;gAeMziAFOU`4&#jc8YnB>2vQ;+ z2P7mMfrPLuE0AR5{|zJ= Date: Fri, 21 Nov 2025 16:52:03 +0530 Subject: [PATCH 08/12] Add existing listener experience --- .../service/GcloudPubsubServiceBuilder.java | 71 +++++++++++++++++-- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java index 20d9568c78..4993f25b5c 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java @@ -24,13 +24,20 @@ import io.ballerina.servicemodelgenerator.extension.model.ServiceInitModel; import io.ballerina.servicemodelgenerator.extension.model.Value; import io.ballerina.servicemodelgenerator.extension.model.context.AddServiceInitModelContext; +import io.ballerina.servicemodelgenerator.extension.model.context.GetServiceInitModelContext; import io.ballerina.servicemodelgenerator.extension.model.context.ModelFromSourceContext; +import io.ballerina.servicemodelgenerator.extension.util.ListenerUtil; import org.eclipse.lsp4j.TextEdit; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import static io.ballerina.servicemodelgenerator.extension.model.ServiceInitModel.KEY_CONFIGURE_LISTENER; +import static io.ballerina.servicemodelgenerator.extension.model.ServiceInitModel.KEY_EXISTING_LISTENER; +import static io.ballerina.servicemodelgenerator.extension.model.ServiceInitModel.KEY_LISTENER_VAR_NAME; import static io.ballerina.servicemodelgenerator.extension.util.Constants.ARG_TYPE_LISTENER_PARAM_INCLUDED_FIELD; import static io.ballerina.servicemodelgenerator.extension.util.Constants.CLOSE_BRACE; import static io.ballerina.servicemodelgenerator.extension.util.Constants.GCLOUD_PUBSUB; @@ -43,9 +50,11 @@ import static io.ballerina.servicemodelgenerator.extension.util.Constants.VALUE_TYPE_EXPRESSION; import static io.ballerina.servicemodelgenerator.extension.util.DatabindUtil.addDataBindingParam; import static io.ballerina.servicemodelgenerator.extension.util.JmsUtil.ON_MESSAGE_FUNCTION_NAME; +import static io.ballerina.servicemodelgenerator.extension.util.JmsUtil.buildListenerChoice; import static io.ballerina.servicemodelgenerator.extension.util.JmsUtil.buildServiceCodeEdits; import static io.ballerina.servicemodelgenerator.extension.util.ServiceModelUtils.getRequiredFunctionsForServiceType; import static io.ballerina.servicemodelgenerator.extension.util.Utils.FunctionAddContext.TRIGGER_ADD; +import static io.ballerina.servicemodelgenerator.extension.util.Utils.applyEnabledChoiceProperty; import static io.ballerina.servicemodelgenerator.extension.util.Utils.buildServiceAnnotation; /** @@ -58,10 +67,40 @@ public final class GcloudPubsubServiceBuilder extends AbstractServiceBuilder { private static final String TYPE_PUBSUB_SERVICE_CONFIG = "pubsub:ServiceConfig"; private static final String SERVICE_TYPE = "pubsub:Service"; private static final String PROPERTY_CREDENTIALS = "credentials"; + private static final String PROPERTY_PROJECT_ID = "project"; private static final String PROPERTY_AUTH = "auth"; private static final String AUTH_CONFIG_TEMPLATE = "{path: \"%s\"}"; public static final String PAYLOAD_FIELD_NAME = "data"; public static final String TYPE_PREFIX = "Message"; + private static final String LABEL_GCLOUD_PUBSUB = "Google Cloud Pub/Sub"; + + // Listener configuration property keys + private static final String[] LISTENER_CONFIG_KEYS = { + PROPERTY_PROJECT_ID, PROPERTY_CREDENTIALS, KEY_LISTENER_VAR_NAME + }; + + @Override + public ServiceInitModel getServiceInitModel(GetServiceInitModelContext context) { + ServiceInitModel serviceInitModel = super.getServiceInitModel(context); + if (serviceInitModel == null) { + return null; + } + + Map properties = serviceInitModel.getProperties(); + Set listeners = ListenerUtil.getCompatibleListeners(context.moduleName(), + context.semanticModel(), context.project()); + + if (!listeners.isEmpty()) { + Map listenerProps = new LinkedHashMap<>(); + for (String key : LISTENER_CONFIG_KEYS) { + listenerProps.put(key, properties.remove(key)); + } + Value choicesProperty = buildListenerChoice(listenerProps, listeners, LABEL_GCLOUD_PUBSUB); + properties.put(KEY_CONFIGURE_LISTENER, choicesProperty); + } + + return serviceInitModel; + } @Override public Map> addServiceInitSource(AddServiceInitModelContext context) { @@ -70,12 +109,31 @@ public Map> addServiceInitSource(AddServiceInitModelConte applyCredentialsProperty(properties); - ListenerDTO listenerDTO = buildListenerDTO(context); + if (!properties.containsKey(KEY_CONFIGURE_LISTENER)) { + return addServiceWithNewListener(context); + } + + applyEnabledChoiceProperty(serviceInitModel, KEY_CONFIGURE_LISTENER); + + ListenerDTO listenerDTO; + applyCredentialsProperty(properties); + if (properties.containsKey(KEY_EXISTING_LISTENER)) { + listenerDTO = new ListenerDTO(context.serviceInitModel().getModuleName(), + properties.get(KEY_EXISTING_LISTENER).getValue(), ""); + } else { + listenerDTO = buildListenerDTO(context); + } String serviceCode = buildPubsubServiceCode(serviceInitModel, listenerDTO); return buildServiceCodeEdits(context, serviceCode, null); } + private Map> addServiceWithNewListener(AddServiceInitModelContext context) { + ListenerDTO listenerDTO = buildListenerDTO(context); + String serviceCode = buildPubsubServiceCode(context.serviceInitModel(), listenerDTO); + return buildServiceCodeEdits(context, serviceCode, null); + } + private void applyCredentialsProperty(Map properties) { if (!properties.containsKey(PROPERTY_CREDENTIALS)) { return; @@ -110,16 +168,19 @@ private String buildPubsubServiceCode(ServiceInitModel serviceInitModel, Listene List functionsStr = AbstractServiceBuilder.buildMethodDefinitions( functions, TRIGGER_ADD, new HashMap<>()); - return NEW_LINE - + listenerDTO.listenerDeclaration() - + NEW_LINE - + serviceAnnotation + String code = NEW_LINE; + if (!listenerDTO.listenerDeclaration().isEmpty()) { + code += listenerDTO.listenerDeclaration() + NEW_LINE; + } + code += serviceAnnotation + SERVICE + SPACE + SERVICE_TYPE + SPACE + ON + SPACE + listenerDTO.listenerVarName() + SPACE + OPEN_BRACE + NEW_LINE + String.join(TWO_NEW_LINES, functionsStr) + NEW_LINE + CLOSE_BRACE + NEW_LINE; + + return code; } @Override From 06bcff85538d09a90f30dc38480a36d1fe36b1dd Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Mon, 24 Nov 2025 14:55:03 +0530 Subject: [PATCH 09/12] Rename classes support camelcase --- .../extension/builder/FunctionBuilderRouter.java | 4 ++-- .../extension/builder/ServiceBuilderRouter.java | 4 ++-- ...unctionBuilder.java => GcloudPubSubFunctionBuilder.java} | 6 +++--- ...bServiceBuilder.java => GcloudPubSubServiceBuilder.java} | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/{GcloudPubsubFunctionBuilder.java => GcloudPubSubFunctionBuilder.java} (93%) rename service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/{GcloudPubsubServiceBuilder.java => GcloudPubSubServiceBuilder.java} (99%) diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/FunctionBuilderRouter.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/FunctionBuilderRouter.java index a57926247a..6a3b15fb29 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/FunctionBuilderRouter.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/FunctionBuilderRouter.java @@ -27,7 +27,7 @@ import io.ballerina.projects.Document; import io.ballerina.projects.Project; import io.ballerina.servicemodelgenerator.extension.builder.function.DefaultFunctionBuilder; -import io.ballerina.servicemodelgenerator.extension.builder.function.GcloudPubsubFunctionBuilder; +import io.ballerina.servicemodelgenerator.extension.builder.function.GcloudPubSubFunctionBuilder; import io.ballerina.servicemodelgenerator.extension.builder.function.GraphqlFunctionBuilder; import io.ballerina.servicemodelgenerator.extension.builder.function.HttpFunctionBuilder; import io.ballerina.servicemodelgenerator.extension.builder.function.KafkaFunctionBuilder; @@ -74,7 +74,7 @@ public class FunctionBuilderRouter { put(MCP, McpFunctionBuilder::new); put(KAFKA, KafkaFunctionBuilder::new); put(SOLACE, SolaceFunctionBuilder::new); - put(GCLOUD_PUBSUB, GcloudPubsubFunctionBuilder::new); + put(GCLOUD_PUBSUB, GcloudPubSubFunctionBuilder::new); }}; private static NodeBuilder getFunctionBuilder(String protocol) { diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/ServiceBuilderRouter.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/ServiceBuilderRouter.java index 75ec5d4ff9..48c298a68c 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/ServiceBuilderRouter.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/ServiceBuilderRouter.java @@ -27,7 +27,7 @@ import io.ballerina.servicemodelgenerator.extension.builder.service.AiChatServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.AsbServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.DefaultServiceBuilder; -import io.ballerina.servicemodelgenerator.extension.builder.service.GcloudPubsubServiceBuilder; +import io.ballerina.servicemodelgenerator.extension.builder.service.GcloudPubSubServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.GraphqlServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.HttpServiceBuilder; import io.ballerina.servicemodelgenerator.extension.builder.service.KafkaServiceBuilder; @@ -85,7 +85,7 @@ public class ServiceBuilderRouter { put(KAFKA, KafkaServiceBuilder::new); put(ASB, AsbServiceBuilder::new); put(SOLACE, SolaceServiceBuilder::new); - put(GCLOUD_PUBSUB, GcloudPubsubServiceBuilder::new); + put(GCLOUD_PUBSUB, GcloudPubSubServiceBuilder::new); }}; public static ServiceNodeBuilder getServiceBuilder(String protocol) { diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubsubFunctionBuilder.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubSubFunctionBuilder.java similarity index 93% rename from service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubsubFunctionBuilder.java rename to service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubSubFunctionBuilder.java index 0b87b30967..57483e1b03 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubsubFunctionBuilder.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/function/GcloudPubSubFunctionBuilder.java @@ -27,8 +27,8 @@ import java.util.List; import java.util.Map; -import static io.ballerina.servicemodelgenerator.extension.builder.service.GcloudPubsubServiceBuilder.PAYLOAD_FIELD_NAME; -import static io.ballerina.servicemodelgenerator.extension.builder.service.GcloudPubsubServiceBuilder.TYPE_PREFIX; +import static io.ballerina.servicemodelgenerator.extension.builder.service.GcloudPubSubServiceBuilder.PAYLOAD_FIELD_NAME; +import static io.ballerina.servicemodelgenerator.extension.builder.service.GcloudPubSubServiceBuilder.TYPE_PREFIX; import static io.ballerina.servicemodelgenerator.extension.util.Constants.GCLOUD_PUBSUB; /** @@ -36,7 +36,7 @@ * * @since 1.4.0 */ -public final class GcloudPubsubFunctionBuilder extends AbstractFunctionBuilder { +public final class GcloudPubSubFunctionBuilder extends AbstractFunctionBuilder { private static final String REQUIRED_PARAM_TYPE = "pubsub:Message"; diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubSubServiceBuilder.java similarity index 99% rename from service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java rename to service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubSubServiceBuilder.java index 4993f25b5c..dbae883e01 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubsubServiceBuilder.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubSubServiceBuilder.java @@ -62,7 +62,7 @@ * * @since 1.4.0 */ -public final class GcloudPubsubServiceBuilder extends AbstractServiceBuilder { +public final class GcloudPubSubServiceBuilder extends AbstractServiceBuilder { private static final String TYPE_PUBSUB_SERVICE_CONFIG = "pubsub:ServiceConfig"; private static final String SERVICE_TYPE = "pubsub:Service"; From 9588800286f14603d65d231ffae9f448d0e63c9a Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Mon, 24 Nov 2025 14:56:25 +0530 Subject: [PATCH 10/12] Make caller param required --- .../src/main/resources/service_artifacts.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json b/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json index fb9d8e9d35..ff3eaab22a 100644 --- a/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json +++ b/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json @@ -1507,7 +1507,7 @@ "name": "caller", "label": "Caller", "description": "The Pub/Sub caller to acknowledge the message", - "kind": "OPTIONAL", + "kind": "REQUIRED", "type": "pubsub:Caller", "defaultValue": "", "importStatements": "", From 7139c68d599eac4b00fa3304b59a7b1ae1da5e54 Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Mon, 24 Nov 2025 14:57:00 +0530 Subject: [PATCH 11/12] Update wrapper type name and cred file desc --- .../service/GcloudPubSubServiceBuilder.java | 3 ++- .../src/main/resources/service-index.sqlite | Bin 126976 -> 126976 bytes .../src/main/resources/service_artifacts.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubSubServiceBuilder.java b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubSubServiceBuilder.java index dbae883e01..09b09147ac 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubSubServiceBuilder.java +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/main/java/io/ballerina/servicemodelgenerator/extension/builder/service/GcloudPubSubServiceBuilder.java @@ -71,7 +71,7 @@ public final class GcloudPubSubServiceBuilder extends AbstractServiceBuilder { private static final String PROPERTY_AUTH = "auth"; private static final String AUTH_CONFIG_TEMPLATE = "{path: \"%s\"}"; public static final String PAYLOAD_FIELD_NAME = "data"; - public static final String TYPE_PREFIX = "Message"; + public static final String TYPE_PREFIX = "PubSubMessage"; private static final String LABEL_GCLOUD_PUBSUB = "Google Cloud Pub/Sub"; // Listener configuration property keys @@ -142,6 +142,7 @@ private void applyCredentialsProperty(Map properties) { Value credentialsValue = properties.get(PROPERTY_CREDENTIALS); if (credentialsValue == null || credentialsValue.getValue() == null || credentialsValue.getValue().isEmpty()) { + properties.remove(PROPERTY_CREDENTIALS); return; } diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite b/service-model-generator/modules/service-model-generator-ls-extension/src/main/resources/service-index.sqlite index ae6f6d36bd7fc7f47095e85b159e6e8fe946387c..c2b9e73909cf4fa7d6452260e83c5c2c7b809b98 100644 GIT binary patch delta 301 zcmZp8z~1nHeS(xwyP0RfCx1qn&HvxC3#1)m;Lqp#$ydmG45&g1 zh-HCT9E!z&v?LIN>;|zVfEZ*A2+II5NG(W@2oQtRfYb^DF-RUH4&sB=ZOvhf;^k=6 zkmqAyR5qV}nU_(OL%}Q9-!CL1b$UZ3qs(@3KE`fWjv&{-P|qM&m+h;98KY#DvotUY nEN8g@A_^D1Sye? z0}@h>KtfoS6-YAj{|1r_lAC!7{_8W!Z{}I>$)8br^Z)nk0%@li`1ARG@)h!)2C9$% zVvs%JP%H+d<$zcQh(T;gAeMziAFOU`4r3JW!~j-~ zMrCF0PERhdIFQl~e}WR%%1&d1p8%Hban;_2__=(BxQFk_U=a+V2<0?Szn jK*R+Qv4K%wJIe#cKm6MkFtQ44XKi5o&%d4J08jt`JI^`r diff --git a/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json b/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json index ff3eaab22a..bbe40fd7d3 100644 --- a/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json +++ b/service-model-generator/modules/service-model-index-generator/src/main/resources/service_artifacts.json @@ -1577,7 +1577,7 @@ "selections": [] }, "credentials": { - "label": "User Credentials", + "label": "User Credentials JSON", "description": "The service account credentials JSON file path", "defaultValue": "", "placeholder": "\"/path/to/credentials.json\"", From 1f1d61f0ed5aeaa31a42bbd651d96553c05f128e Mon Sep 17 00:00:00 2001 From: Radith Samarakoon Date: Tue, 25 Nov 2025 22:37:53 +0530 Subject: [PATCH 12/12] Add tests for gcloud pubsub artifact --- .../config/gcp_service_model_1.json | 95 ++++ .../config/gcp_service_model_2.json | 152 ++++++ .../source/sample2/main.bal | 2 + .../config/gcp_service_model.json | 469 ++++++++++++++++++ .../source/sample14/Ballerina.toml | 6 + .../source/sample14/main.bal | 17 + 6 files changed, 741 insertions(+) create mode 100644 service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/config/gcp_service_model_1.json create mode 100644 service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/config/gcp_service_model_2.json create mode 100644 service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/config/gcp_service_model.json create mode 100644 service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/source/sample14/Ballerina.toml create mode 100644 service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/source/sample14/main.bal diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/config/gcp_service_model_1.json b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/config/gcp_service_model_1.json new file mode 100644 index 0000000000..56a1eeb4c8 --- /dev/null +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/config/gcp_service_model_1.json @@ -0,0 +1,95 @@ +{ + "description": "Test getting gcloud.pubsub service init model", + "filePath": "sample1/main.bal", + "orgName": "ballerinax", + "pkgName": "gcloud.pubsub", + "moduleName": "gcloud.pubsub", + "response": { + "serviceInitModel": { + "id": "17", + "displayName": "Google Cloud Pub/Sub Event Integration", + "description": "Create a service to handle messages from a Google Cloud Pub/Sub subscription", + "orgName": "ballerinax", + "packageName": "gcloud.pubsub", + "moduleName": "gcloud.pubsub", + "version": "1.0.0", + "type": "gcloud.pubsub", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_gcloud.pubsub_1.0.0.png", + "properties": { + "project": { + "metadata": { + "label": "Project ID", + "description": "The Google Cloud project ID" + }, + "codedata": { + "argType": "LISTENER_PARAM_REQUIRED" + }, + "placeholder": "\"my-gcp-project\"", + "valueType": "EXPRESSION", + "valueTypeConstraint": "string", + "value": "", + "items": [], + "typeMembers": [], + "enabled": true, + "editable": true, + "optional": false, + "advanced": false + }, + "subscription": { + "metadata": { + "label": "Subscription", + "description": "The name of the Pub/Sub subscription to pull messages from" + }, + "codedata": { + "argType": "SOURCE_ANNOTATION" + }, + "placeholder": "", + "valueType": "EXPRESSION", + "valueTypeConstraint": "string", + "value": "", + "items": [], + "typeMembers": [], + "enabled": true, + "editable": true, + "optional": false, + "advanced": false + }, + "credentials": { + "metadata": { + "label": "User Credentials JSON", + "description": "The service account credentials JSON file path" + }, + "codedata": { + "argType": "LISTENER_PARAM_INCLUDED_FIELD" + }, + "placeholder": "\"/path/to/credentials.json\"", + "valueType": "FILE_SELECT", + "valueTypeConstraint": "string", + "value": "", + "items": [], + "typeMembers": [], + "enabled": true, + "editable": true, + "optional": true, + "advanced": true + }, + "listenerVarName": { + "metadata": { + "label": "Listener Name", + "description": "Provide a name for the listener being created" + }, + "codedata": { + "type": "LISTENER_VAR_NAME" + }, + "valueType": "IDENTIFIER", + "valueTypeConstraint": "Global", + "value": "pubsubListener", + "enabled": true, + "editable": true, + "optional": false, + "advanced": true + } + } + } + } +} diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/config/gcp_service_model_2.json b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/config/gcp_service_model_2.json new file mode 100644 index 0000000000..e6b822b4c6 --- /dev/null +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/config/gcp_service_model_2.json @@ -0,0 +1,152 @@ +{ + "description": "Test getting gcloud.pubsub service init model with existing listeners", + "filePath": "sample2/main.bal", + "orgName": "ballerinax", + "pkgName": "gcloud.pubsub", + "moduleName": "gcloud.pubsub", + "response": { + "serviceInitModel": { + "id": "17", + "displayName": "Google Cloud Pub/Sub Event Integration", + "description": "Create a service to handle messages from a Google Cloud Pub/Sub subscription", + "orgName": "ballerinax", + "packageName": "gcloud.pubsub", + "moduleName": "gcloud.pubsub", + "version": "1.0.0", + "type": "gcloud.pubsub", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_gcloud.pubsub_1.0.0.png", + "properties": { + "subscription": { + "metadata": { + "label": "Subscription", + "description": "The name of the Pub/Sub subscription to pull messages from" + }, + "codedata": { + "argType": "SOURCE_ANNOTATION" + }, + "placeholder": "", + "valueType": "EXPRESSION", + "valueTypeConstraint": "string", + "value": "", + "items": [], + "typeMembers": [], + "enabled": true, + "editable": true, + "optional": false, + "advanced": false + }, + "configureListener": { + "metadata": { + "label": "Use Existing Listener", + "description": "Use Existing Listener or Create New Listener" + }, + "valueType": "CHOICE", + "value": true, + "choices": [ + { + "metadata": { + "label": "Use Existing Listener", + "description": "Use Existing Listener" + }, + "valueType": "FORM", + "value": "true", + "properties": { + "existingListener": { + "metadata": { + "label": "Select Listener", + "description": "Select from the existing Google Cloud Pub/Sub listeners" + }, + "valueType": "SINGLE_SELECT", + "value": "pubsubListener", + "items": [ + "pubsubListener" + ], + "enabled": true, + "editable": true, + "optional": false, + "advanced": false + } + }, + "enabled": false, + "editable": false, + "optional": false, + "advanced": false + }, + { + "metadata": { + "label": "Create New Listener", + "description": "Create a new Google Cloud Pub/Sub listener" + }, + "valueType": "FORM", + "value": "true", + "properties": { + "project": { + "metadata": { + "label": "Project ID", + "description": "The Google Cloud project ID" + }, + "codedata": { + "argType": "LISTENER_PARAM_REQUIRED" + }, + "placeholder": "\"my-gcp-project\"", + "valueType": "EXPRESSION", + "valueTypeConstraint": "string", + "value": "", + "items": [], + "typeMembers": [], + "enabled": true, + "editable": true, + "optional": false, + "advanced": false + }, + "credentials": { + "metadata": { + "label": "User Credentials JSON", + "description": "The service account credentials JSON file path" + }, + "codedata": { + "argType": "LISTENER_PARAM_INCLUDED_FIELD" + }, + "placeholder": "\"/path/to/credentials.json\"", + "valueType": "FILE_SELECT", + "valueTypeConstraint": "string", + "value": "", + "items": [], + "typeMembers": [], + "enabled": true, + "editable": true, + "optional": true, + "advanced": true + }, + "listenerVarName": { + "metadata": { + "label": "Listener Name", + "description": "Provide a name for the listener being created" + }, + "codedata": { + "type": "LISTENER_VAR_NAME" + }, + "valueType": "IDENTIFIER", + "valueTypeConstraint": "Global", + "value": "pubsubListenerResult", + "enabled": true, + "editable": true, + "optional": false, + "advanced": true + } + }, + "enabled": false, + "editable": false, + "optional": false, + "advanced": false + } + ], + "enabled": true, + "editable": true, + "optional": false, + "advanced": true + } + } + } + } +} diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/source/sample2/main.bal b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/source/sample2/main.bal index 32719957a6..7a3963260a 100644 --- a/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/source/sample2/main.bal +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_service_init_model/source/sample2/main.bal @@ -1,6 +1,8 @@ import ballerinax/rabbitmq; import ballerinax/solace; +import ballerinax/gcloud.pubsub; listener rabbitmq:Listener orderListener = new (rabbitmq:DEFAULT_HOST, 5671); listener rabbitmq:Listener deliveryListener = new (rabbitmq:DEFAULT_HOST, 5671); listener solace:Listener solaceListener = new ("smf://localhost:55554", messageVpn = "default"); +listener pubsub:Listener pubsubListener = new ("project1", auth = {path: "path/to/auth.json"}); diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/config/gcp_service_model.json b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/config/gcp_service_model.json new file mode 100644 index 0000000000..37f9de7d89 --- /dev/null +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/config/gcp_service_model.json @@ -0,0 +1,469 @@ +{ + "filePath": "sample14/main.bal", + "description": "Test the generation of service model for a gcloud.pubsub service", + "start": { + "line": 4, + "offset": 0 + }, + "end": { + "line": 16, + "offset": 1 + }, + "response": { + "id": "17", + "name": "Google Cloud Pub/Sub Event Integration", + "type": "gcloud.pubsub", + "displayName": "Google Cloud Pub/Sub Event Integration", + "moduleName": "gcloud.pubsub", + "orgName": "ballerinax", + "version": "1.0.0", + "packageName": "gcloud.pubsub", + "listenerProtocol": "pubsub", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_gcloud.pubsub_1.0.0.png", + "documentation": { + "metadata": { + "label": "Description", + "description": "The description of the class" + }, + "codedata": { + "type": "DOCUMENTATION" + }, + "valueType": "STRING", + "valueTypeConstraint": "string", + "enabled": true, + "editable": true, + "optional": true, + "advanced": false + }, + "properties": { + "listener": { + "metadata": { + "label": "Listener", + "description": "The Listener to be bound with the service" + }, + "codedata": { + "type": "LISTENER" + }, + "placeholder": "", + "valueType": "SINGLE_SELECT_LISTENER", + "valueTypeConstraint": "pubsub:Listener", + "value": "pubsubListener", + "values": [], + "items": [ + "pubsubListener" + ], + "properties": { + "pubsubListener": { + "codedata": { + "lineRange": { + "fileName": "main.bal", + "startLine": { + "line": 7, + "offset": 26 + }, + "endLine": { + "line": 7, + "offset": 40 + } + } + }, + "enabled": false, + "editable": false, + "optional": false, + "advanced": false + } + }, + "enabled": true, + "editable": true, + "optional": false, + "advanced": false + }, + "serviceType": { + "metadata": { + "label": "Service Type", + "description": "The name of the service contract type" + }, + "codedata": { + "type": "SERVICE_TYPE" + }, + "placeholder": "Service", + "valueType": "SINGLE_SELECT", + "valueTypeConstraint": "string", + "value": "Service", + "items": [ + "", + "Service" + ], + "enabled": false, + "editable": false, + "optional": false, + "advanced": false + }, + "readOnlyMetadata": { + "codedata": { + "type": "READONLY" + }, + "placeholder": "false", + "valueType": "SINGLE_SELECT", + "value": { + "Subscription": [ + "sub1" + ] + }, + "enabled": true, + "editable": true, + "optional": false, + "advanced": false + }, + "annotServiceConfig": { + "metadata": { + "label": "Service Configuration", + "description": "Define advanced subscription configurations" + }, + "codedata": { + "type": "ANNOTATION_ATTACHMENT", + "originalName": "ServiceConfig" + }, + "placeholder": "{}", + "valueType": "EXPRESSION", + "valueTypeConstraint": "pubsub:ServiceConfiguration", + "value": "{\n subscription: \"sub1\"\n}", + "values": [], + "typeMembers": [ + { + "type": "ServiceConfiguration", + "packageInfo": "ballerinax:gcloud.pubsub:1.0.0", + "kind": "RECORD_TYPE", + "selected": true + } + ], + "enabled": true, + "editable": true, + "optional": true, + "advanced": false + } + }, + "codedata": { + "lineRange": { + "fileName": "main.bal", + "startLine": { + "line": 4, + "offset": 0 + }, + "endLine": { + "line": 16, + "offset": 1 + } + }, + "orgName": "ballerinax", + "packageName": "gcloud.pubsub", + "moduleName": "gcloud.pubsub" + }, + "functions": [ + { + "metadata": { + "label": "onMessage", + "description": "The `onMessage` remote method will be triggered when a message is received from a Google Cloud Pub/Sub subscription" + }, + "kind": "REMOTE", + "name": { + "metadata": { + "label": "onMessage", + "description": "The `onMessage` remote method will be triggered when a message is received from a Google Cloud Pub/Sub subscription" + }, + "placeholder": "onMessage", + "valueType": "IDENTIFIER", + "value": "onMessage", + "enabled": true, + "editable": false, + "optional": false, + "advanced": false + }, + "parameters": [ + { + "metadata": { + "label": "message", + "description": "The received Pub/Sub message" + }, + "kind": "REQUIRED", + "type": { + "metadata": { + "label": "Parameter Type", + "description": "The type of the parameter" + }, + "placeholder": "pubsub:Message", + "valueType": "TYPE", + "value": "pubsub:Message", + "enabled": true, + "editable": true, + "optional": true, + "advanced": false + }, + "name": { + "metadata": { + "label": "message", + "description": "The received Pub/Sub message" + }, + "placeholder": "message", + "valueType": "IDENTIFIER", + "value": "message", + "enabled": true, + "editable": true, + "optional": false, + "advanced": false + }, + "defaultValue": { + "metadata": { + "label": "Default Value", + "description": "The default value of the parameter" + }, + "placeholder": "", + "valueType": "EXPRESSION", + "value": "", + "enabled": true, + "editable": true, + "optional": true, + "advanced": false + }, + "enabled": false, + "editable": true, + "optional": false, + "advanced": false, + "hidden": false, + "isGraphqlId": false + }, + { + "metadata": { + "label": "caller", + "description": "The Pub/Sub caller to acknowledge the message" + }, + "kind": "REQUIRED", + "type": { + "metadata": { + "label": "Parameter Type", + "description": "The type of the parameter" + }, + "placeholder": "pubsub:Caller", + "valueType": "TYPE", + "value": "pubsub:Caller", + "enabled": true, + "editable": false, + "optional": true, + "advanced": false + }, + "name": { + "metadata": { + "label": "caller", + "description": "The Pub/Sub caller to acknowledge the message" + }, + "placeholder": "caller", + "valueType": "IDENTIFIER", + "value": "caller", + "enabled": true, + "editable": false, + "optional": false, + "advanced": false + }, + "defaultValue": { + "metadata": { + "label": "Default Value", + "description": "The default value of the parameter" + }, + "placeholder": "", + "valueType": "EXPRESSION", + "value": "", + "enabled": true, + "editable": true, + "optional": true, + "advanced": false + }, + "enabled": true, + "editable": true, + "optional": false, + "advanced": false, + "hidden": false, + "isGraphqlId": false + }, + { + "metadata": { + "label": "Data Binding", + "description": "Data binding parameter" + }, + "kind": "DATA_BINDING", + "type": { + "valueType": "TYPE", + "value": "", + "enabled": true, + "editable": false, + "optional": false, + "advanced": false + }, + "name": { + "valueType": "IDENTIFIER", + "value": "message", + "enabled": true, + "editable": false, + "optional": false, + "advanced": false + }, + "enabled": false, + "editable": true, + "optional": false, + "advanced": false, + "hidden": false, + "isGraphqlId": false + } + ], + "returnType": { + "hasError": true, + "isGraphqlId": false, + "metadata": { + "label": "Return Type", + "description": "The return type of the function" + }, + "placeholder": "error?", + "valueType": "TYPE", + "value": "error?", + "enabled": true, + "editable": false, + "optional": true, + "advanced": false + }, + "enabled": true, + "optional": false, + "editable": false, + "canAddParameters": false, + "codedata": { + "lineRange": { + "fileName": "main.bal", + "startLine": { + "line": 8, + "offset": 4 + }, + "endLine": { + "line": 14, + "offset": 5 + } + }, + "moduleName": "gcloud.pubsub" + }, + "properties": { + "wrapperTypeName": { + "value": "PubSubMessage", + "enabled": false, + "editable": false, + "optional": false, + "advanced": false + }, + "payloadFieldName": { + "value": "data", + "enabled": false, + "editable": false, + "optional": false, + "advanced": false + }, + "canDataBind": { + "value": "true", + "enabled": false, + "editable": false, + "optional": false, + "advanced": false + } + } + }, + { + "metadata": { + "label": "onError", + "description": "Triggers when an error occurs during message processing" + }, + "kind": "REMOTE", + "name": { + "metadata": { + "label": "onError", + "description": "Triggers when an error occurs during message processing" + }, + "placeholder": "onError", + "valueType": "IDENTIFIER", + "value": "onError", + "enabled": true, + "editable": false, + "optional": false, + "advanced": false + }, + "parameters": [ + { + "metadata": { + "label": "error", + "description": "The error occurred during message processing" + }, + "kind": "REQUIRED", + "type": { + "metadata": { + "label": "Parameter Type", + "description": "The type of the parameter" + }, + "placeholder": "pubsub:Error", + "valueType": "TYPE", + "value": "pubsub:Error", + "enabled": true, + "editable": false, + "optional": true, + "advanced": false + }, + "name": { + "metadata": { + "label": "error", + "description": "The error occurred during message processing" + }, + "placeholder": "error", + "valueType": "IDENTIFIER", + "value": "error", + "enabled": true, + "editable": true, + "optional": false, + "advanced": false + }, + "defaultValue": { + "metadata": { + "label": "Default Value", + "description": "The default value of the parameter" + }, + "placeholder": "", + "valueType": "EXPRESSION", + "value": "", + "enabled": true, + "editable": true, + "optional": true, + "advanced": false + }, + "enabled": true, + "editable": true, + "optional": false, + "advanced": false, + "hidden": false, + "isGraphqlId": false + } + ], + "returnType": { + "hasError": true, + "isGraphqlId": false, + "metadata": { + "label": "Return Type", + "description": "The return type of the function" + }, + "placeholder": "error?", + "valueType": "TYPE", + "value": "error?", + "enabled": true, + "editable": false, + "optional": true, + "advanced": false + }, + "enabled": false, + "optional": true, + "editable": false, + "canAddParameters": false + } + ] + } +} diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/source/sample14/Ballerina.toml b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/source/sample14/Ballerina.toml new file mode 100644 index 0000000000..4c600c3a38 --- /dev/null +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/source/sample14/Ballerina.toml @@ -0,0 +1,6 @@ +[package] +org = "ballerina" +name = "service_designer" +version = "0.1.0" + +bi = true diff --git a/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/source/sample14/main.bal b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/source/sample14/main.bal new file mode 100644 index 0000000000..a266f29b67 --- /dev/null +++ b/service-model-generator/modules/service-model-generator-ls-extension/src/test/resources/get_sm_from_source/source/sample14/main.bal @@ -0,0 +1,17 @@ +import ballerinax/gcloud.pubsub; + +listener pubsub:Listener pubsubListener = new ("project1", auth = {path: "/Users/radith/Desktop/auth.json"}); + +@pubsub:ServiceConfig { + subscription: "sub1" +} +service pubsub:Service on pubsubListener { + remote function onMessage(PubSubMessage message, pubsub:Caller caller) returns error? { + do { + } on fail error err { + // handle error + return error("unhandled error", err); + } + } + +}