public static HtmlString ModalFor <T>(string methodName, bool?isStatic = null, string elementId = null, bool includeWithBinding = true) { ClassViewModel classModel = ReflectionRepository.Global.GetClassViewModel <T>(); MethodViewModel method = classModel.ClientMethods.FirstOrDefault(f => (isStatic == null || isStatic == f.IsStatic) && f.Name == methodName); return(ModalFor(method, elementId, includeWithBinding)); }
/// <summary> /// Write the metadata for a specific parameter to a specific method /// </summary> private void WriteMethodParameterMetadata(TypeScriptCodeBuilder b, MethodViewModel method, ParameterViewModel parameter) { using (b.Block($"{parameter.JsVariable}:", ',')) { WriteValueCommonMetadata(b, parameter); b.StringProp("role", "value"); } }
public void Create(MethodViewModel methodVM) { var Method = new Method { MethodName = methodVM.MethodName }; unitOfWork.MethodRepository.Insert(Method); unitOfWork.Save(); }
public MethodView(MethodViewModel viewModel) { InitializeComponent(); DataContext = ViewModel = viewModel; ViewModel.Close += (sender, eventArgs) => { DialogResult = eventArgs.IsAccepted; Close(); }; }
private void ProcessStandardParameters(Operation operation, MethodViewModel method) { // Remove behaviors - behaviors accept no input from the client. foreach (var paramVm in method.Parameters.Where(p => p.Type.IsA(typeof(IBehaviors <>)))) { operation.Parameters.Remove(operation.Parameters.Single(p => p.Name == paramVm.Name)); } foreach (var paramVm in method.Parameters.Where(p => p.Type.IsA <IDataSourceParameters>())) { var paramType = paramVm.Type.ClassViewModel; // Join our ParameterViewModels with the Swashbuckle IParameters. var paramsUnion = paramType.ClientProperties.Join( operation.Parameters.OfType <NonBodyParameter>(), p => p.Name, p => p.Name, (pvm, nbp) => (PropViewModel: pvm, OperationParam: nbp) ); foreach (var noSetterProp in paramsUnion.Where(p => // Remove params that have no setter. // Honestly I'm clueless why this isn't default behavior, but OK. !p.PropViewModel.HasSetter // Remove the string "DataSource" parameter that is redundant with our data source model binding functionality. || (p.PropViewModel.Name == nameof(IDataSourceParameters.DataSource) && p.OperationParam.Type == "string") // Remove "Filter" - we'll enumerate all available filter params (for lack of a better solution with OpenAPI 2.0 || (p.PropViewModel.Name == nameof(IFilterParameters.Filter) && p.OperationParam.Type == "object") )) { operation.Parameters.Remove(noSetterProp.OperationParam); } if (paramVm.Type.IsA <IFilterParameters>()) { // Kind of a hacky way to get this, but we don't have a whole lot of other context here. var modelType = method.Parent.Type .GenericArgumentsFor(typeof(IntelliTect.Coalesce.Api.Controllers.BaseApiController <, ,>)) ?[0]; if (modelType != null) { foreach (var filterProp in modelType.ClassViewModel.ClientProperties.Where(p => p.IsUrlFilterParameter)) { operation.Parameters.Add(new NonBodyParameter { In = "query", Name = $"filter.{filterProp.Name}", Required = false, Description = $"Filters results by values contained in property '{filterProp.JsonName}'.", Type = "string", }); } } } } }
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs <object> e) { if (e.NewValue is ClassViewModel) { SelectedClass = e.NewValue as ClassViewModel; } if (e.NewValue is MethodViewModel) { SelectedMethod = e.NewValue as MethodViewModel; } }
/// <summary> /// For a method invocation controller action, writes the actual invocation of the method. /// </summary> /// <param name="b">The CodeBuilder to write to</param> /// <param name="method">The method to be invoked</param> /// <param name="owningMember"> /// The member on which to invoke the method. /// This could be an variable holding an instance of a type, or a class reference if the method is static. /// </param> public void WriteMethodInvocation(CSharpCodeBuilder b, MethodViewModel method, string owningMember) { // Don't try to store the result in the variable if the method returns void. if (!method.TaskUnwrappedReturnType.IsVoid) { b.Append($"var {MethodResultVar} = "); } var awaitSymbol = method.IsAwaitable ? "await " : ""; b.Line($"{awaitSymbol}{owningMember}.{method.Name}({method.CsArguments});"); }
private void OnMethodClick(UILogic uiLogic, MethodViewModel mvm) { string sourcePath = uiLogic.GetSourceFilePathForMethod(mvm); _dte.ItemOperations.OpenFile(sourcePath); //TODO when the adornment layer is stable, enable this line moving. //var firstLineOfMethod = uiLogic.GetFirstLineOfMethod(mvm); //var commandName = "Edit.GoTo " + firstLineOfMethod; //_dte.ExecuteCommand(commandName); }
public void Update(MethodViewModel methodVM) { var Method = new Method { MethodId = methodVM.MethodId, MethodName = methodVM.MethodName }; unitOfWork.MethodRepository.Update(Method); unitOfWork.Save(); }
public static HtmlString ModalFor(MethodViewModel method, string elementId = null, bool includeWithBinding = true) { if (elementId == null) { elementId = $"method-{method.Name}"; } var b = new HtmlCodeBuilder(); b.Line($"<!-- Modal for method: {method.Name} -->"); var withBinding = includeWithBinding ? $"with: {method.JsVariable}" : null; using (b .TagBlock("div", new { @class = "modal fade", id = elementId, tabindex = -1, role = "dialog", data_bind = withBinding }) .TagBlock("div", "modal-dialog") .TagBlock("div", "modal-content") ) { using (b.TagBlock("div", "modal-header")) { b.Line("<button type='button' class='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>×</span></button>"); b.Line($"<h4 class='modal-title'>{method.Name.ToProperCase()}</h4>"); } using (b.TagBlock("div", "modal-body form-horizontal", "with: args")) { foreach (ParameterViewModel arg in method.ClientParameters) { using (b.TagBlock("div", "form-group")) { b.Line($"<label class='col-md-4 control-label'>{arg.Name.ToProperCase()}</label>"); using (b.TagBlock("div", "col-md-8")) { b.Line($"<input type='text' class='form-control' data-bind='value: {arg.JsVariable}'>"); } } } } using (b.TagBlock("div", "modal-footer")) { b.Line("<button type='button' class='btn btn-default' data-dismiss='modal'>Cancel</button>"); b.Line(@"<button type='button' class='btn btn-primary btn-ok'"); b.Indented(@"data-bind=""click: invokeWithArgs.bind(this, args, function(){jQuery($element).closest('.modal').modal('hide')}, null)"">"); b.Indented("OK"); b.Line("</button>"); } } return(new HtmlString(b.ToString())); }
public ActionResult Edit(MethodViewModel methodVM) { try { if (ModelState.IsValid) { methodServices.Update(methodVM); } return(RedirectToAction("Index")); } catch { return(View()); } }
public static Method ToMethod(MethodViewModel model) { if (model == null) { return(null); } return(new Method { Id = model.Id, VariableId = model.Variable?.Id, IsStatic = model.IsStatic, IsConstructor = model.IsConstructor, TypeName = model.TypeName, Name = model.Name, Variable = VariableMapper.ToVariable(model.Variable), Arguments = model.Arguments? .Select(x => ArgumentMapper.ToArgument(x, model.Id)) .ToList() }); }
private XModule CreateModule(AssemblyViewModel assembly, ClassViewModel cls, MethodViewModel method, TCFolder parent) { var module = XModule.Create(); module.ParentFolder.Set(parent); module.Name = method.FriendlyName ?? method.Name; var assemblyParam = module.CreateTechnicalIDParam(); var classNameParam = module.CreateTechnicalIDParam(); var methodNameParam = module.CreateTechnicalIDParam(); var engineParam = module.CreateConfigurationParam(); var setParam = module.CreateConfigurationParam(); assemblyParam.Name = "LibraryFile"; assemblyParam.Value = assembly.FilePath; classNameParam.Name = "ClassName"; classNameParam.Value = cls.FullName; methodNameParam.Name = "MethodName"; methodNameParam.Value = method.Name; engineParam.Name = "Engine"; engineParam.Value = "TosCode"; setParam.Name = "SpecialExecutionTask"; setParam.Value = "Execute"; foreach (var parameter in method.Parameters) { CreateModuleAttribute(module, parameter); } if (method.ReturnType != typeof(void)) { var returnAttribute = module.CreateModuleAttribute(); BuildModuleAttribute(returnAttribute, "Result", method.ReturnType, 1, true, XTestStepActionMode.Verify); var returnParam = returnAttribute.CreateConfigurationParam(); returnParam.Name = "Result"; returnParam.Value = "true"; } return(module); }
///-------------------------------------------------------------------------------- /// <summary>This method handles the execution of the new command.</summary> /// /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments.</param> ///-------------------------------------------------------------------------------- private void NewExecuted(object sender, ExecutedRoutedEventArgs e) { Point currentPosition = MouseUtilities.GetMousePosition(this); MethodsViewModel items = DataContext as MethodsViewModel; EntityDiagramControl diagram = VisualItemHelper.VisualUpwardSearch <EntityDiagramControl>(this) as EntityDiagramControl; DiagramEntityViewModel diagramEntityView = diagram.DataContext as DiagramEntityViewModel; if (items != null && diagramEntityView != null) { dialog = new Window(); dialog.Height = 450 * UserSettingsHelper.Instance.ControlSize; dialog.Width = 400 * UserSettingsHelper.Instance.ControlSize; dialog.Left = Math.Max(currentPosition.X, 20); dialog.Top = Math.Max(currentPosition.Y, 20); dialog.Content = new MethodDialogControl(); Method newItem = new Method(); newItem.MethodID = Guid.NewGuid(); newItem.Solution = items.Solution; newItem.Entity = items.Entity; //newItem.ReferenceEntity = diagramEntityView.DiagramEntity.Entity; MethodViewModel newItemView = new MethodViewModel(newItem, items.Solution); newItemView.IsFreeDialog = true; dialog.DataContext = newItemView; dialog.Title = newItemView.Title; newItemView.RequestClose += new EventHandler(Item_RequestClose); #region protected #endregion protected dialog.ShowDialog(); if (newItemView.IsOK == true) { items.AddMethod(newItemView); } dialog.Close(); dialog = null; e.Handled = true; return; } }
private static void WriteApiEndpointFunction(TypeScriptCodeBuilder b, ClassViewModel model, MethodViewModel method) { var returnIsListResult = method.ReturnsListResult; string signature = string.Concat(method.ClientParameters.Select(f => $"{f.Name}: {new VueType(f.Type).TsType("$models")} | null, ")) + "$config?: AxiosRequestConfig"; if (method.IsModelInstanceMethod) { signature = $"id: {new VueType(model.PrimaryKey.Type).TsType(null)}, " + signature; } string resultType = method.TransportTypeGenericParameter.IsVoid ? $"{method.TransportType}<void>" : $"{method.TransportType}<{new VueType(method.TransportTypeGenericParameter).TsType("$models")}>"; using (b.Block($"public {method.JsVariable}({signature}): AxiosPromise<{resultType}>")) { b.Line($"const $method = this.$metadata.methods.{method.JsVariable}"); using (b.Block($"const $params = ")) { if (method.IsModelInstanceMethod) { b.Line($"id,"); } foreach (var param in method.ClientParameters) { b.Line($"{param.JsVariable},"); } } b.Line("return this.$invoke($method, $params, $config)"); } // Line between methods b.Line(); }
private MemberViewModel GetViewModelForMethod(MethodInfo methodInfo) { MemberViewModel vm; if (_viewModelsDictionary.ContainsKey(methodInfo)) { var typeViewModel = GetViewModelForType(methodInfo.DeclaringType); vm = _viewModelsDictionary[methodInfo]; vm.Background = typeViewModel.Background; vm.Foreground = typeViewModel.Foreground; return(vm); } if (IsPropertyAccessor(methodInfo)) { var propertyInfo = Helper.GetAccessorProperty(methodInfo.MemberReference); var typeViewModel = GetViewModelForType(propertyInfo.DeclaringType); if (_viewModelsDictionary.ContainsKey(propertyInfo)) { vm = _viewModelsDictionary[propertyInfo]; vm.Background = typeViewModel.Background; vm.Foreground = typeViewModel.Foreground; return(vm); } var pvm = new PropertyViewModel(propertyInfo) { Background = typeViewModel.Background, Foreground = typeViewModel.Foreground, ToolTip = typeViewModel.Name }; _viewModelsDictionary.Add(propertyInfo, pvm); return(pvm); } if (IsEventAccessor(methodInfo)) { var eventInfo = Helper.GetAccessorEvent(methodInfo.MemberReference); var typeViewModel = GetViewModelForType(eventInfo.DeclaringType); if (_viewModelsDictionary.ContainsKey(eventInfo)) { vm = _viewModelsDictionary[eventInfo]; vm.Background = typeViewModel.Background; vm.Foreground = typeViewModel.Foreground; return(vm); } var evm = new EventViewModel(eventInfo) { Background = typeViewModel.Background, Foreground = typeViewModel.Foreground, ToolTip = typeViewModel.Name }; _viewModelsDictionary.Add(eventInfo, evm); return(evm); } var tvm = GetViewModelForType(methodInfo.DeclaringType); var mvm = new MethodViewModel(methodInfo) { Background = tvm.Background, Foreground = tvm.Foreground, ToolTip = tvm.Name }; _viewModelsDictionary.Add(methodInfo, mvm); return(mvm); }
private void ProcessDataSources(Operation operation, OperationFilterContext context, MethodViewModel method) { // In all reality, there will only ever be one data source parameter per action, // but might as well not make assumptions if we don't have to. foreach (var paramVm in method.Parameters.Where(p => p.Type.IsA(typeof(IDataSource <>)))) { var dataSourceParam = operation.Parameters .OfType <NonBodyParameter>() .Single(p => p.Name == paramVm.Name && p.Type == "object"); var declaredFor = paramVm.GetAttributeValue <DeclaredForAttribute>(a => a.DeclaredFor) ?? paramVm.Type.GenericArgumentsFor(typeof(IDataSource <>)).Single(); var dataSources = declaredFor.ClassViewModel.ClientDataSources(ReflectionRepository); dataSourceParam.Type = "string"; dataSourceParam.Enum = (new object[] { IntelliTect.Coalesce.Api.DataSources.DataSourceFactory.DefaultSourceName }) .Concat(dataSources.Select(ds => ds.ClientTypeName as object)) .ToList(); foreach (var dataSource in dataSources) { foreach (var dsParam in dataSources.SelectMany(s => s.DataSourceParameters)) { var schema = context.SchemaRegistry.GetOrRegister(dsParam.Type.TypeInfo); operation.Parameters.Add(new NonBodyParameter { In = "query", Name = $"{paramVm.Name}.{dsParam.Name}", Required = false, Description = $"Used by Data Source {dataSource.ClientTypeName}", // Data source parameters only support a fairly simple set of params, // and all *should* be representable with just these two values. Type = schema.Type, Format = schema.Format, }); } } } }
/// <summary> /// Adds a button for a method /// </summary> /// <param name="method"></param> /// <returns></returns> public static string MethodHelper(MethodViewModel method) { var callFunction = method.ClientParameters.Any() ? "invokeWithPrompts" : "invoke"; return($@"<a href=""#"" data-bind='click: function(){{ {method.JsVariable}.{callFunction}() }}'>{method.DisplayName}</a>"); }
///-------------------------------------------------------------------------------- /// <summary>This method handles dropping items onto the treeview.</summary> /// /// <param name="data">The data as a viewmodel.</param> ///-------------------------------------------------------------------------------- protected void Paste(IWorkspaceViewModel data) { SolutionViewModel solution; ProjectViewModel assembly; DatabaseSourceViewModel databaseSource; AuditPropertyViewModel auditProperty; FeatureViewModel feature; EntityViewModel entity; CollectionViewModel collectionProperty; PropertyReferenceViewModel derivedProperty; PropertyViewModel property; EntityReferenceViewModel referenceProperty; MethodViewModel method; ParameterViewModel parameter; IndexViewModel index; IndexPropertyViewModel indexProperty; RelationshipViewModel relationship; RelationshipPropertyViewModel relationshipProperty; if (data is ProjectViewModel) { // process assembly drop assembly = data as ProjectViewModel; solution = DataContext as SolutionViewModel; if (solution == null) { // get parent solution solution = ViewModelHelper.FindParentView <SolutionViewModel>(this); } if (solution == null) { // invalid drop location HandleInvalidDrop(assembly); } else if (solution.ProjectsFolder.Projects.Find("Name", assembly.Name) != null) { // duplicate item found, cannot drop if (assembly != DataContext as ProjectViewModel) { HandleDuplicateDrop(assembly); } } else { // do the drop ProjectViewModel view = solution.ProjectsFolder.PasteProject(assembly, true); view.ShowInTreeView(); } } else if (data is DatabaseSourceViewModel) { // process databaseSource drop databaseSource = data as DatabaseSourceViewModel; solution = DataContext as SolutionViewModel; if (solution == null) { // get parent solution solution = ViewModelHelper.FindParentView <SolutionViewModel>(this); } if (solution == null) { // invalid drop location HandleInvalidDrop(databaseSource); } else if (solution.SpecificationSourcesFolder.DatabaseSources.Find("Name", databaseSource.Name) != null) { // duplicate item found, cannot drop if (databaseSource != DataContext as DatabaseSourceViewModel) { HandleDuplicateDrop(databaseSource); } } else { // do the drop DatabaseSourceViewModel view = solution.SpecificationSourcesFolder.PasteDatabaseSource(databaseSource, true); view.ShowInTreeView(); } } else if (data is AuditPropertyViewModel) { // process auditProperty drop auditProperty = data as AuditPropertyViewModel; solution = DataContext as SolutionViewModel; if (solution == null) { // get parent solution solution = ViewModelHelper.FindParentView <SolutionViewModel>(this); } if (solution == null) { // invalid drop location HandleInvalidDrop(auditProperty); } else if (solution.AuditPropertiesFolder.AuditProperties.Find("Name", auditProperty.Name) != null) { // duplicate item found, cannot drop if (auditProperty != DataContext as AuditPropertyViewModel) { HandleDuplicateDrop(auditProperty); } } else { // do the drop AuditPropertyViewModel view = solution.AuditPropertiesFolder.PasteAuditProperty(auditProperty, true); view.ShowInTreeView(); } } else if (data is FeatureViewModel) { // process feature drop feature = data as FeatureViewModel; solution = DataContext as SolutionViewModel; if (solution == null) { // get parent solution solution = ViewModelHelper.FindParentView <SolutionViewModel>(this); } if (solution == null) { // invalid drop location HandleInvalidDrop(feature); } else if (solution.FeaturesFolder.Features.Find("Name", feature.Name) != null) { // duplicate item found, cannot drop if (feature != DataContext as FeatureViewModel) { HandleDuplicateDrop(feature); } } else { // do the drop Mouse.OverrideCursor = Cursors.Wait; FeatureViewModel view = solution.FeaturesFolder.PasteFeature(feature, true); view.ShowInTreeView(); solution.Refresh(true); Mouse.OverrideCursor = null; } } else if (data is EntityViewModel) { // process entity drop entity = data as EntityViewModel; feature = DataContext as FeatureViewModel; if (feature == null) { // get parent feature feature = ViewModelHelper.FindParentView <FeatureViewModel>(this); } if (feature == null) { // invalid drop location HandleInvalidDrop(entity); } else if (feature.Entities.Find("Name", entity.Name) != null) { // duplicate item found, cannot drop if (entity != DataContext as EntityViewModel) { HandleDuplicateDrop(entity); } } else { // do the drop Mouse.OverrideCursor = Cursors.Wait; EntityViewModel view = feature.PasteEntity(entity, true); view.ShowInTreeView(); solution = ViewModelHelper.FindParentView <SolutionViewModel>(this); if (solution != null) { solution.Refresh(true); } Mouse.OverrideCursor = null; } } else if (data is CollectionViewModel) { // process collectionProperty drop collectionProperty = data as CollectionViewModel; entity = DataContext as EntityViewModel; if (entity == null) { // get parent entity entity = ViewModelHelper.FindParentView <EntityViewModel>(this); } if (entity == null) { // invalid drop location HandleInvalidDrop(collectionProperty); } else if (entity.CollectionsFolder.Collections.Find("Name", collectionProperty.Name) != null) { // duplicate item found, cannot drop if (collectionProperty != DataContext as CollectionViewModel) { HandleDuplicateDrop(collectionProperty); } } else { // do the drop CollectionViewModel view = entity.CollectionsFolder.PasteCollection(collectionProperty, true); view.ShowInTreeView(); } } else if (data is PropertyReferenceViewModel) { // process derivedProperty drop derivedProperty = data as PropertyReferenceViewModel; entity = DataContext as EntityViewModel; if (entity == null) { // get parent entity entity = ViewModelHelper.FindParentView <EntityViewModel>(this); } if (entity == null) { // invalid drop location HandleInvalidDrop(derivedProperty); } else if (entity.PropertyReferencesFolder.PropertyReferences.Find("Name", derivedProperty.Name) != null) { // duplicate item found, cannot drop if (derivedProperty != DataContext as PropertyReferenceViewModel) { HandleDuplicateDrop(derivedProperty); } } else { // do the drop PropertyReferenceViewModel view = entity.PropertyReferencesFolder.PastePropertyReference(derivedProperty, true); view.ShowInTreeView(); } } else if (data is PropertyViewModel) { // process property drop property = data as PropertyViewModel; entity = DataContext as EntityViewModel; if (entity == null) { // get parent entity entity = ViewModelHelper.FindParentView <EntityViewModel>(this); } if (entity == null) { // invalid drop location HandleInvalidDrop(property); } else if (entity.PropertiesFolder.Properties.Find("Name", property.Name) != null) { // duplicate item found, cannot drop if (property != DataContext as PropertyViewModel) { HandleDuplicateDrop(property); } } else { // do the drop PropertyViewModel view = entity.PropertiesFolder.PasteProperty(property, true); solution = ViewModelHelper.FindParentView <SolutionViewModel>(this); if (solution != null) { solution.Refresh(true); } view.ShowInTreeView(); } } else if (data is EntityReferenceViewModel) { // process referenceProperty drop referenceProperty = data as EntityReferenceViewModel; entity = DataContext as EntityViewModel; if (entity == null) { // get parent entity entity = ViewModelHelper.FindParentView <EntityViewModel>(this); } if (entity == null) { // invalid drop location HandleInvalidDrop(referenceProperty); } else if (entity.EntityReferencesFolder.EntityReferences.Find("Name", referenceProperty.Name) != null) { // duplicate item found, cannot drop if (referenceProperty != DataContext as EntityReferenceViewModel) { HandleDuplicateDrop(referenceProperty); } } else { // do the drop EntityReferenceViewModel view = entity.EntityReferencesFolder.PasteEntityReference(referenceProperty, true); view.ShowInTreeView(); } } else if (data is MethodViewModel) { // process method drop method = data as MethodViewModel; entity = DataContext as EntityViewModel; if (entity == null) { // get parent entity entity = ViewModelHelper.FindParentView <EntityViewModel>(this); } if (entity == null) { // invalid drop location HandleInvalidDrop(method); } else if (entity.MethodsFolder.Methods.Find("Name", method.Name) != null) { // duplicate item found, cannot drop if (method != DataContext as MethodViewModel) { HandleDuplicateDrop(method); } } else { // do the drop MethodViewModel view = entity.MethodsFolder.PasteMethod(method, true); view.ShowInTreeView(); } } else if (data is ParameterViewModel) { // process parameter drop parameter = data as ParameterViewModel; method = DataContext as MethodViewModel; if (method == null) { // get parent method method = ViewModelHelper.FindParentView <MethodViewModel>(this); } if (method == null) { // invalid drop location HandleInvalidDrop(parameter); } else if (method.Parameters.Find("Name", parameter.Name) != null) { // duplicate item found, cannot drop if (parameter != DataContext as ParameterViewModel) { HandleDuplicateDrop(parameter); } } else { // do the drop ParameterViewModel view = method.PasteParameter(parameter, true); view.ShowInTreeView(); } } else if (data is IndexViewModel) { // process index drop index = data as IndexViewModel; entity = DataContext as EntityViewModel; if (entity == null) { // get parent entity entity = ViewModelHelper.FindParentView <EntityViewModel>(this); } if (entity == null) { // invalid drop location HandleInvalidDrop(index); } else if (entity.IndexesFolder.Indexes.Find("Name", index.Name) != null) { // duplicate item found, cannot drop if (index != DataContext as IndexViewModel) { HandleDuplicateDrop(index); } } else { // do the drop IndexViewModel view = entity.IndexesFolder.PasteIndex(index, true); view.ShowInTreeView(); } } else if (data is IndexPropertyViewModel) { // process index drop indexProperty = data as IndexPropertyViewModel; index = DataContext as IndexViewModel; if (index == null) { // get parent index index = ViewModelHelper.FindParentView <IndexViewModel>(this); } if (index == null) { // invalid drop location HandleInvalidDrop(indexProperty); } else if (index.IndexProperties.Find("Name", indexProperty.Name) != null) { // duplicate item found, cannot drop if (indexProperty != DataContext as IndexPropertyViewModel) { HandleDuplicateDrop(indexProperty); } } else { // do the drop IndexPropertyViewModel view = index.PasteIndexProperty(indexProperty, true); view.ShowInTreeView(); } } else if (data is RelationshipViewModel) { // process relationship drop relationship = data as RelationshipViewModel; entity = DataContext as EntityViewModel; if (entity == null) { // get parent entity entity = ViewModelHelper.FindParentView <EntityViewModel>(this); } if (entity == null) { // invalid drop location HandleInvalidDrop(relationship); } else if (entity.RelationshipsFolder.Relationships.Find("Name", relationship.Name) != null) { // duplicate item found, cannot drop if (relationship != DataContext as RelationshipViewModel) { HandleDuplicateDrop(relationship); } } else { // do the drop RelationshipViewModel view = entity.RelationshipsFolder.PasteRelationship(relationship, true); view.ShowInTreeView(); } } else if (data is RelationshipPropertyViewModel) { // process relationshipProperty drop relationshipProperty = data as RelationshipPropertyViewModel; relationship = DataContext as RelationshipViewModel; if (relationship == null) { // get parent relationship relationship = ViewModelHelper.FindParentView <RelationshipViewModel>(this); } if (relationship == null) { // invalid drop location HandleInvalidDrop(relationshipProperty); } else if (relationship.RelationshipProperties.Find("Name", relationshipProperty.Name) != null) { // duplicate item found, cannot drop if (relationshipProperty != DataContext as RelationshipPropertyViewModel) { HandleDuplicateDrop(relationshipProperty); } } else { // do the drop RelationshipPropertyViewModel view = relationship.PasteRelationshipProperty(relationshipProperty, true); view.ShowInTreeView(); } } }
/// <summary> /// Adds a button for a method /// </summary> /// <param name="method"></param> /// <returns></returns> public static string MethodHelper(MethodViewModel method) { return($@"<a href=""#"" data-bind = 'click: {method.JsVariableUi}'>{method.DisplayName}</a>"); }
/// <summary> /// For a method invocation controller action, writes the block of code that /// transforms the raw return result of the method into an object appropriate /// for returning from the action and ultimately serialization to the client. /// </summary> /// <param name="b">The code builder to write to.</param> /// <param name="method">The method whose results need transforming.</param> public void WriteMethodResultProcessBlock(CSharpCodeBuilder b, MethodViewModel method) { var resultVar = MethodResultVar; var resultProp = "Object"; var resultType = method.ResultType; // For methods that return ItemResult<> or ListResult<>, grab the core result data from the appropriate member. if (method.TaskUnwrappedReturnType.IsA(typeof(ItemResult <>))) { resultVar += ".Object"; } else if (method.ReturnsListResult) { resultProp = "List"; resultVar += ".List"; } if (method.TaskUnwrappedReturnType.IsA <ApiResult>()) { // For any ApiResult return type, pass it to our ApiResult ctor to grab the WasSuccessful and Message props. // For list results, this also copies paging information. b.Line($"var result = new {method.ApiActionReturnTypeDeclaration}({MethodResultVar});"); } else { // Return type isn't an ApiResult - just create a 'blank' object. b.Line($"var result = new {method.ApiActionReturnTypeDeclaration}();"); } if (resultType.IsVoid) { // If the result type is void, there's no methodResult to stick on our return object. // We'll just return the plain 'result' object. } else if (resultType.IsA <ApiResult>()) { // If the resultType (not the ReturnType) is an API result, this means that the method is either: // 1) Returning exactly ApiResult and not a derived type of it. This is OK (I guess...); in this case, we can safely ignore because there is no Object on it to process. // 2) Returning an ItemResult<T> or ListResult<T> where T : ApiResult. This is bonkers. Don't do this. } else if (resultType.IsCollection) { if (resultType.PureType.HasClassViewModel && !resultType.PureType.ClassViewModel.IsDto) { // Return type is a collection of models that need to be mapped to a DTO. b.Line("var mappingContext = new MappingContext(User, \"\");"); // ToList the result (because it might be IQueryable - we need to execute the query before mapping) b.Append($"result.{resultProp} = {resultVar}?.ToList().Select(o => "); b.Append($"Mapper.MapToDto<{resultType.PureType.ClassViewModel.FullyQualifiedName}, {resultType.PureType.ClassViewModel.DtoName}>"); // Only attempt to pull the include tree out of the result if the user actually typed their return type as an IQueryable. var includeTreeForMapping = resultType.IsA <IQueryable>() ? $"includeTree ?? ({resultVar} as IQueryable)?.GetIncludeTree()" : "includeTree"; b.Append($"(o, mappingContext, {includeTreeForMapping})).ToList();"); b.Line(); } else { // Return type is a collection of primitives or IClassDtos. // This ToList() may end up being redundant, but it is guaranteed to be safe. // The minimum type required here that handles all cases is IList<T> (required by a ListResult<T> return type). b.Line($"result.{resultProp} = {resultVar}?.ToList();"); } } else { if (resultType.HasClassViewModel && !resultType.ClassViewModel.IsDto) { // Return type is a model that needs to be mapped to a DTO. b.Line("var mappingContext = new MappingContext(User, \"\");"); b.Line($"result.{resultProp} = Mapper.MapToDto<{resultType.ClassViewModel.FullyQualifiedName}, {resultType.ClassViewModel.DtoName}>({resultVar}, mappingContext, includeTree);"); } else { // Return type is primitive or an IClassDto b.Line($"result.{resultProp} = {resultVar};"); } } b.Append("return result;"); }
/// <summary> /// Writes the class for invoking the given client method, /// as well as the property for the default instance of this class. /// </summary> /// <param name="b"></param> /// <param name="method"></param> /// <param name="parentClassName"></param> /// <param name="methodLoadsParent"> /// If true, code emitted for methods that load their parents' type should /// load the method's parent with the results of the method when called. /// </param> /// <param name="parentLoadHasIdParameter"> /// If true, calls to this.parent.load() will pass null as the first arg and a callback as the second. /// </param> public void WriteClientMethodDeclaration( TypeScriptCodeBuilder b, MethodViewModel method, string parentClassName, bool methodLoadsParent = false, bool parentLoadHasIdParameter = false ) { var model = this.Model; var isService = method.Parent.IsService; var returnIsListResult = method.ReturnsListResult; string reloadArg = !isService ? ", reload" : ""; string reloadParam = !isService ? ", reload: boolean = true" : ""; string callbackAndReloadParam = $"callback?: (result: {method.ResultType.TsType}) => void{reloadParam}"; // Default instance of the method class. b.Line("/**"); b.Indented($"Methods and properties for invoking server method {method.Name}."); if (method.Comment.Length > 0) { b.Indented(method.Comment); } b.Line($"*/"); b.Line($"public readonly {method.JsVariable} = new {parentClassName}.{method.Name}(this);"); string methodBaseClass = returnIsListResult ? "ClientListMethod" : "ClientMethod"; // Not wrapping this in a using since it is used by nearly this entire method. Will manually dispose. var classBlock = b.Block( $"public static {method.Name} = class {method.Name} extends Coalesce.{methodBaseClass}<{parentClassName}, {method.ResultType.TsType}>", ';'); b.Line($"public readonly name = '{method.Name}';"); b.Line($"public readonly verb = '{method.ApiActionHttpMethodName}';"); if (method.ResultType.IsCollection || method.ResultType.IsArray) { b.Line($"public result: {method.ResultType.TsKnockoutType()} = {method.ResultType.ObservableConstructorCall()};"); } // ---------------------- // Standard invoke method - all CS method parameters as TS method parameters. // ---------------------- b.Line(); b.Line($"/** Calls server method ({method.Name}) with the given arguments */"); string parameters = ""; parameters = string.Join(", ", method.ClientParameters.Select(f => f.Type.TsDeclarationPlain(f.Name) + " | null")); if (!string.IsNullOrWhiteSpace(parameters)) { parameters = parameters + ", "; } parameters = parameters + callbackAndReloadParam; using (b.Block($"public invoke = ({parameters}): JQueryPromise<any> =>", ';')) { string jsPostObject = "{ "; if (method.IsModelInstanceMethod) { jsPostObject = jsPostObject + "id: this.parent[this.parent.primaryKeyName]()"; if (method.Parameters.Any()) { jsPostObject = jsPostObject + ", "; } } string TsConversion(ParameterViewModel param) { string argument = param.JsVariable; if (param.Type.HasClassViewModel) { return($"{argument} ? {argument}.saveToDto() : null"); } if (param.Type.IsDate) { return($"{argument} ? {argument}.format() : null"); } return(argument); } jsPostObject += string.Join(", ", method.ClientParameters.Select(f => $"{f.JsVariable}: {TsConversion(f)}")); jsPostObject += " }"; b.Line($"return this.invokeWithData({jsPostObject}, callback{reloadArg});"); } // ---------------------- // Members for methods with parameters only. if (method.ClientParameters.Any()) { b.Line(); // ---------------------- // Args class, and default instance b.Line($"/** Object that can be easily bound to fields to allow data entry for the method's parameters */"); b.Line($"public args = new {method.Name}.Args(); "); using (b.Block("public static Args = class Args", ';')) { foreach (var arg in method.ClientParameters) { b.Line($@"public {arg.JsVariable}: {arg.Type.TsKnockoutType(true)} = {arg.Type.ObservableConstructorCall()};"); } } // Gets the js arguments to pass to a call to this.invoke(...) string JsArguments(string obj = "") { string result; if (obj != "") { result = string.Join(", ", method.ClientParameters.Select(f => $"{obj}.{f.JsVariable}()")); } else { result = string.Join(", ", method.ClientParameters.Select(f => obj + f.JsVariable)); } if (!string.IsNullOrEmpty(result)) { result = result + ", "; } result = result + "callback"; return(result); } // ---------------------- // invokeWithArgs method // ---------------------- b.Line(); b.Line($"/** Calls server method ({method.Name}) with an instance of {method.Name}.Args, or the value of this.args if not specified. */"); // We can't explicitly declare the type of the args parameter here - TypeScript doesn't allow it. // Thankfully, we can implicitly type using the default. using (b.Block($"public invokeWithArgs = (args = this.args, {callbackAndReloadParam}): JQueryPromise<any> =>")) { b.Line($"return this.invoke({JsArguments("args")}{reloadArg});"); } // ---------------------- // invokeWithPrompts method // ---------------------- b.Line(); b.Line("/** Invokes the method after displaying a browser-native prompt for each argument. */"); using (b.Block($"public invokeWithPrompts = ({callbackAndReloadParam}): JQueryPromise<any> | undefined =>", ';')) { b.Line($"var $promptVal: string | null = null;"); foreach (var param in method.ClientParameters.Where(f => f.ConvertsFromJsString)) { b.Line($"$promptVal = {$"prompt('{param.Name.ToProperCase()}')"};"); // AES 1/23/2018 - why do we do this? what about optional params where no value is desired? b.Line($"if ($promptVal === null) return;"); b.Line($"var {param.Name}: {param.Type.TsType} = {param.Type.TsConvertFromString("$promptVal")};"); } // For all parameters that can't convert from a string (is this even possible with what we support for method parameters now?), // pass null as the value. I guess we just let happen what's going to happen? Again, not sure when this case would ever be hit. foreach (var param in method.ClientParameters.Where(f => !f.ConvertsFromJsString)) { b.Line($"var {param.Name}: null = null;"); } b.Line($"return this.invoke({JsArguments("")}{reloadArg});"); } } // ---------------------- // Method response handler - highly dependent on what the response type actually is. // ---------------------- b.Line(); using (b.Block($"protected loadResponse = (data: Coalesce.{(returnIsListResult ? "List" : "Item")}Result, {callbackAndReloadParam}) =>", ';')) { var incomingMainData = returnIsListResult ? "data.list || []" : "data.object"; if (method.ResultType.IsCollection && method.ResultType.PureType.HasClassViewModel) { // Collection of objects that have TypeScript ViewModels. This could be an entity or an external type. // If the models have a key, rebuild our collection using that key so that we can reuse objects when the data matches. var keyNameArg = method.ResultType.PureType.ClassViewModel.PrimaryKey != null ? $"'{method.ResultType.PureType.ClassViewModel.PrimaryKey.JsVariable}'" : "null"; b.Line($"Coalesce.KnockoutUtilities.RebuildArray(this.result, {incomingMainData}, {keyNameArg}, ViewModels.{method.ResultType.PureType.ClassViewModel.ClientTypeName}, this, true);"); } else if (method.ResultType.HasClassViewModel) { // Single view model return type. b.Line("if (!this.result()) {"); b.Indented($"this.result(new ViewModels.{method.ResultType.PureType.ClassViewModel.ClientTypeName}({incomingMainData}));"); b.Line("} else {"); b.Indented($"this.result().loadFromDto({incomingMainData});"); b.Line("}"); } else { // Uninteresting return type. Either void, a primitive, or a collection of primitives. // In any case, regardless of the type of the 'result' observable, this is how we set the results. b.Line($"this.result({incomingMainData});"); } if (isService) { b.Line("if (typeof(callback) == 'function')"); b.Indented("callback(this.result());"); } else if (method.ResultType.EqualsType(method.Parent.Type) && methodLoadsParent) { // The return type is the type of the method's parent. Load the parent with the results of the method. // Parameter 'reload' has no meaning here, since we're reloading the object with the result of the method. b.Line($"this.parent.loadFromDto({incomingMainData}, true)"); using (b.Block("if (typeof(callback) == 'function')")) { b.Line($"callback(this.result());"); } } else { // We're not loading the results into the method's parent. This is by far the more common case. b.Line("if (reload) {"); // If reload is true, invoke the load function on the method's parent to reload its latest state from the server. // Only after that's done do we call the method-completion callback. // Store the result locally in case it somehow gets changed by us reloading the parent. b.Indented("var result = this.result();"); b.Indented($"this.parent.load({(parentLoadHasIdParameter ? "null, " : "")}typeof(callback) == 'function' ? () => callback(result) : undefined);"); b.Line("} else if (typeof(callback) == 'function') {"); // If we're not reloading, just call the callback now. b.Indented("callback(this.result());"); b.Line("}"); } } // End of the method class declaration. classBlock.Dispose(); }
/// <summary> /// Write the metadata for an entire method /// </summary> private void WriteClassMethodMetadata(TypeScriptCodeBuilder b, ClassViewModel model, MethodViewModel method) { using (b.Block($"{method.JsVariable}:", ',')) { b.StringProp("name", method.JsVariable); b.StringProp("displayName", method.DisplayName); b.StringProp("transportType", method.TransportType.ToString().Replace("Result", "").ToLower()); using (b.Block("params:", ',')) { // TODO: should we be writing out the implicit 'id' param as metadata? Or handling some other way? // If we do keep it as metadata, should probably add some prop to mark it as what it is. if (method.IsModelInstanceMethod) { using (b.Block($"id:", ',')) { b.StringProp("name", "id"); b.StringProp("displayName", "Primary Key"); // TODO: Is this what we want? Also, i18n. b.StringProp("role", "value"); WriteTypeCommonMetadata(b, model.PrimaryKey.Type); } } foreach (var param in method.ClientParameters) { WriteMethodParameterMetadata(b, method, param); } } using (b.Block("return:", ',')) { b.StringProp("name", "$return"); b.StringProp("displayName", "Result"); // TODO: i18n b.StringProp("role", "value"); WriteTypeCommonMetadata(b, method.ResultType); } } }
private static void WriteApiEndpointFunction(TypeScriptCodeBuilder b, ClassViewModel model, MethodViewModel method) { var returnIsListResult = method.ReturnsListResult; string signature = string.Concat(method.ClientParameters.Select(f => $"{f.Name}: {new VueType(f.Type).TsType("$models")} | null, ")) + "$config?: AxiosRequestConfig"; if (method.IsModelInstanceMethod) { signature = $"id: {new VueType(model.PrimaryKey.Type).TsType(null)}, " + signature; } using (b.Block($"public {method.JsVariable}({signature})")) { string resultType = method.TransportTypeGenericParameter.IsVoid ? $"{method.TransportType}<void>" : $"{method.TransportType}<{new VueType(method.TransportTypeGenericParameter).TsType("$models")}>"; b.Line($"const $method = this.$metadata.methods.{method.JsVariable}"); using (b.Block($"const $params = this.$mapParams($method,", ')')) { if (method.IsModelInstanceMethod) { b.Line($"id,"); } foreach (var param in method.ClientParameters) { b.Line($"{param.JsVariable},"); } } b.Line("return AxiosClient"); using (b.Indented()) { b.Line($".{method.ApiActionHttpMethodName.ToLower()}("); b.Indented($"`/${{this.$metadata.controllerRoute}}/{method.Name}`,"); switch (method.ApiActionHttpMethod) { case DataAnnotations.HttpMethod.Get: case DataAnnotations.HttpMethod.Delete: b.Indented($"this.$options(undefined, $config, $params)"); break; default: b.Indented($"qs.stringify($params),"); b.Indented($"this.$options(undefined, $config)"); break; } b.Line(")"); b.Line($".then<AxiosResponse<{resultType}>>(r => this.$hydrate{method.TransportType}(r, $method.return))"); } } // Line between methods b.Line(); }