Create Custom API V8 in Suite CRM - suitecrm

Good Day!
I am planning to create a custom API for Account module were I am going to get and update data using API. I created a custom field (integration_ref_id) on my Account module this will serve as my identifier if the accounts is existing or not. Check bellow as payload request sample
{
"consent_timestamp": "2021-04-13 09:45:00",
"integration_ref_id": "CUSREF-202104-ABCDEF0000003",
"last_name": "De la Cruz",
"first_name": "Juan",
"birthdate": "1995-03-25",
"mobile_number": "+639171234199",
"email": "juan.delacruz#example.com",
"location_code": "LOC0001",
"street_name": "Rose St.",
"landmarks": "Mcdo",
"gender": "male"
}
Now I created custom routes on my custom folder (custom/application/Ext/Api/V8/Config)
<?php
$app->get('/my-route/{myParam}', 'MyCustomController:myCustomAction');
$app->get('/hello', function() {
return 'Hello World!';
});
$app->get('/getCustomers', 'Api\V8\Controllers\CustomersController:getCustomers');
And I place the controller same path of the routes. (custom/application/Ext/Api/V8/Controller)
And this is my controller code
<?php
namespace Api\V8\Controller;
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
class CustomersController extends BaseController
{
public function getCustomers(Request $request, Response $response, array $args, GetRelationshipParams $params)
{
try {
$jsonResponse = $this->relationshipService->getCustomers($request);
return $this->generateResponse($response, $jsonResponse, 200);
} catch (\Exception $exception) {
return $this->generateErrorResponse($response, $exception, 400);
}
}
}
But I got this Error in postman
I keep searching, but its kinda confusing on my end. BTW I am new on this SuiteCRM
Regards!

Never mind, I was able to figure it out. I just added this code below to my controller CustomeController.php
namespace Api\V8\Controller;
use Slim\Http\Response;
use Slim\Http\Request;
And added this one to my custom controller.php
require 'custom/application/Ext/Api/V8/Controller/CustomersController.php';
use Api\V8\Controller;
use Slim\Container;
Then everything should be working.

Related

404 error when calling endpoints from swagger

I went accross a lot of different SO posts, without finding any that solved my problem, and I feel I am only missing a small thing.
I recently renamed my .net solution and manually added an Angular app in my project following this advice, and the controllers won't work again. I searched for any reluctant data that would still have the old name, but I found nothing like that.
There is a small particularity in my project, I have, in my Controllers folder an Administrators folder and a Clients folder.
Admin controllers are setup like this :
[Controller]
[Route("api/admin/[controller]/[action]")]
public class CareController : Controller
{
// GET requests
[HttpGet]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> GetAll()
{
...
}
...
}
And my client controllers are setup like this :
[Controller]
[Route("api/client/[controller]/[action]")]
public class ClientCareCategoryController : Controller
{
// GET
[HttpGet]
public async Task<IActionResult> GetAll()
{
...
}
}
Please note I deleted the constructor, [ProducesResponseType()], fields and namespace for clarity reasons. The namespaces use the new solution name.
In my Program.cs, I setup my application like this :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(config =>
{
config.SwaggerDoc("Backend", new OpenApiInfo { Title = "Backend", Version = "0.1"});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/Backend/swagger.json", "Backend");
});
}
else
{
app.UseHttpsRedirection();
}
app.MapControllers();
app.Run();
The Spa related config is the exact same as the config specified in the issue I linked above.
I also checked my launchsettings.json file, and I also changed the solution name with the new one.
What I tried
Being helped by previous SO posts, here is what I tried without success using routing middleware and MapControllerRoute
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "client",
pattern: "api/client/{controller}/{action}");
endpoints.MapControllerRoute(
name: "admin",
pattern: "api/admin/{controller}/{action}");
});
In addition to that, I also tried removing "client" from one of the controllers, but it didn't work either.
Thanks a lot for any help. Please tell me if you need any other information.
EDIT :
The issue comes from the SPA. When I launch the app when commenting the app.UseSpa(), I can call my endpoints. But I have no idea why do I have this issue atm.

In Laravel how to json response show in ajax?

Kindly check json response from one to many relation. I want to show this response in ajax. But i don't know how to show in ajax. how to show in success response.
{
"extra":{
"values":[
[
{
"id":9,
"extra_name":"Ihr Dip:",
"status":"open",
"created_at":"2022-04-19T16:47:35.000000Z",
"updated_at":"2022-04-19T16:47:35.000000Z",
"created_by":"1",
"updated_by":null,
"extra":[
{
"id":17,
"description":"mit Kr\u00e4uter-Remoulade",
"status":"open",
"main_extra_id":"9",
"price":"1,00",
"created_at":"2022-04-19T16:47:55.000000Z",
"updated_at":"2022-04-19T16:47:55.000000Z",
"created_by":"1",
"updated_by":null
},
{
"id":18,
"description":"mit Pizza Max Ketchup 40ml",
"status":"open",
"main_extra_id":"9",
"price":"10,70",
"created_at":"2022-04-19T16:48:14.000000Z",
"updated_at":"2022-04-19T16:48:14.000000Z",
"created_by":"1",
"updated_by":null
}
]
}
]
]
}
}
Laravel will automatically serialize your Eloquent models and collections to JSON when they are returned from routes or controllers:
Route::get('users', function () {
return User::all();
});
See below link:
https://laravel.com/docs/9.x/eloquent-serialization#relationships
Another example:
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index(){
return Post::latest()
->with('category', 'author')->get();
}
}

Get array of json object and mapping to model class Laravel

I intend to map the output from external api to object attribute in model.
For examples, data->x to Device->$attribute_x;, data->y to Device->$attribute_y; The output is array of objects.
My expected outcome is extract each of object by access the Device model and its attributes and do some manipulation in model. (fetch once time only from API and format it in different function)
Can someone give some guideline on ways to defining methods/class to achieve?
This is my output from external API:
{
"data": [
{
"x": "1",
"y": "2"
},
{
"x": "11",
"y": "22"
}
]
}
This is model without extend Eloquent in Laravel to extract all data from external API using guzzle .
namespace App;
$client = new \GuzzleHttp\Client([
'base_uri' => 'https://xxxx.com',
'headers' => [
'content_type' => 'application/json',
'accept' => 'application/json'
]]);
$response = $client->get('units');
$data = json_decode($response->getBody());
class Devices
{
protected $attribute_x;
protected $attribute_y;
public static function all(){
}
}
I write code from head, but you can create in Devices following method:
public static function importFromAPI($data) {
$result = [];
foreach($data as $item) {
$dev = new Device;
$dev->attribute_x = $item['x'];
$dev->attribute_y = $item['y'];
$result[] = $dev;
}
return $result;
}
And use it to import Device list from data array from API json
$devices = Devices::importFromAPI($data);
Your Device can extend eloquent Model class as well to have easy access to DB. Above method can be also implemented in separate class e.g ApiService and be renamed to importDevicesFromAPI and contains code to load json and map it to Device objects.

What is the best way to retrieve data using Web API 2 and AngularJS

I am just learning on how to use Web API with angular but I am having a few issues on retrieving data and I would appreciate it if someone could help me.
I have a controller called GetUserController with the following methods.
public class GetUserController : ApiController
{
[HttpGet]
public string GetUserName()
{
var user = "John Doe"
return user;
}
[HttpGet]
public string GetUserName2()
{
string user = "Jane Doe";
return user;
}
}
//Angular side of things
function getUser() {
return $http.get('/api/GetUser').then(function (data) {
return data;
});
}
The above code works fine and and returns the first user from the controller . however when I try to get the second by using the below angular code:
function getUser() {
return $http.get('/api/GetUser/GetUserName2').then(function (data) {
return data;
});
}
This does not work for some reason it says it can't find the GetUserName2 method. Am I missing something ? Please help?
EDIT: The error i'm getting is : Multiple actions were found that match the request
As #shammelburg has pointed out, this is as a result of Web.API not being able to match your request to a controller/method.
It's not so much that it's not RESTful, and has nothing to do with the verb you are using... it's just that an appropriate route map was not found.
Per the accepted answer, you can add another generic route map to enable the method of access you are attempting, however a more specific option exists using attribute routing:-
public class GetUserController : ApiController
{
[Route("api/getuser")]
[HttpGet]
public string GetUserName()
{
var user = "John Doe"
return user;
}
[Route("api/getuser/getusername2")]
[HttpGet]
public string GetUserName2()
{
string user = "Jane Doe";
return user;
}
}
And to enable the use of attribute routes, add this to your WebApiConfig class:-
config.MapHttpAttributeRoutes();
This method allows you to setup specific custom mappings of URLs to controllers/methods at the individual method level, without having to make a global route map that may conflict with something else in your application at a later date.
You can find more info on attribute routing here
Whilst the above will resolve the specific issue you are having, there would in practice be a different way to implement the example you gave:-
public class GetUserController : ApiController
{
[Route("api/user/{id}")]
[HttpGet]
public string GetUserName(int id)
{
// this would be replaced with some sort of data lookup...
var user = "unknown";
if (id == 1) {
user = "John Doe";
} else if (id == 2) {
user = "Jane Doe";
} // and so on...
return user;
}
}
In the above, the URL api/user/x where x is a number, e.g. api/user/1 will match the GetUserName method of the GetUserController and pass the number as an argument to the method.
This would be accessed using something like this in Angular:-
function getUser(id) {
return $http.get('/api/user/' + id).then(function (data) {
return data;
});
}
This is caused because it is not a true RESTful call which use HTTP verbs, GET, POST, PUT, DELETE.
The way to get your code to work is by altering your WebApiConfig.cs file.
From:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
To:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
As you can see we've added the {action} to the routeTemplate which makes this very much like a MVC Controller.
This allows you to call your API methods (GetUserName & GetUserName2) name like you are trying to do in your angular $http function.
Example:
return $http.get('/api/GetUser/GetUserName')
return $http.get('/api/GetUser/GetUserName2')

Restangular with Typescript confusing when 'restangularized' response object

I am using Angular 1.5.x with TypeScript. For accessing a remote API I use restangular. As a summary this is my scenario:
My API has the following resource http://localhost:53384/api/timezones. Sending a request with the verb GET to that url returns a JSON array:
[
{
"code":"Dateline Standard Time",
"name":"(UTC-12:00) International Date Line West"
},
{
"code":"UTC-11",
"name":"(UTC-11:00) Coordinated Universal Time-11"
},
{
"code":"Hawaiian Standard Time",
"name":"(UTC-10:00) Hawaii"
}
]
Now in my client AngularJs application with TypeScript:
Restangular configuration being restangularProvider: restangular.IProvider
restangularProvider.setBaseUrl("http://localhost:53384/api");
The TimeZone object representation in the client side with typescript
module app.blocks {
"use strict";
export class TimeZone {
public code: string;
public name: string;
}
}
Factory(restangular.IService) to wrap the restangular all 'timezones' resource
module app.services {
factory.$inject = ["Restangular"];
function factory(restangular: restangular.IService): restangular.IElement {
return restangular.all("timezones");
}
angular
.module("app.services")
.factory("app.services.TimeZonesRestangular", factory);
}
Service that uses TimeZonesRestangular to wrap its restangular functionality and return chained promises to whoever requests timezones in an asynchronous way
module app.services {
"use strict";
export interface IStaticDataService {
getTimeZones(): ng.IPromise<app.blocks.TimeZone[]>;
}
class StaticDataService implements IStaticDataService {
constructor(private timeZonesRestangular: restangular.IElement) {
}
public getTimeZones(): ng.IPromise<blocks.TimeZone[]> {
return this.timeZonesRestangular.getList()
.then((timeZones: blocks.TimeZone[]) => {
return timeZones;
}, (restangularError: any) => {
throw "Error retrieving time zones. Status: " + restangularError.status;
});
}
}
factory.$inject = ["app.services.TimeZonesRestangular"];
function factory(timeZonesRestangular: restangular.IElement): IStaticDataService {
return new StaticDataService(timeZonesRestangular);
}
angular
.module("app.services")
.factory("app.services.StaticDataService", factory);
}
And finally in the controller using the service to get the 'timezones' asynchronously I have this statement
//..other controller things not relevant for this sample
this.staticDataService.getTimeZones()
.then((timeZones: blocks.TimeZone[]) => {
this.timeZones = timeZones;
});
There are 2 PROBLEMS:
The type definition for restangular (which I installed with tsd install restangular --resolve --save) tells me that the successCallback in the getTimeZones() method is a promiseValue: any[], which is fine because it is indeed an array. I thought it would be an array of TimeZone[] and typescript compiles properly because it accepts any[], but when debuggin I see that the successCallback promised value it's not an array of TimeZone[]. It has the properties I expected (code and name) but it also has many other things restangular-ish. An object within that array looks like this (plus some functions):
{
"code":"Dateline Standard Time",
"name":"(UTC-12:00) International Date Line West",
"route":"timezones",
"reqParams":null,
"restangularized":true,
"fromServer":true,
"parentResource":null,
"restangularCollection":false
}
As per https://github.com/mgonto/restangular/issues/150 it looks as if my response had been "restangularized". Scary description for somebody new to restangular like myself..
What interface in restangular type definition should I use to represent the array of restangularized TimeZone[] ?
Is there any example on how to achieve something similar with TypeScript?
Thank you.
After digging a little bit further I found out that the best way to handle this is by expecting a promised value of type restangular.ICollection (which inherits from IService and Array<any>) so that I can de-restangularize the response like this:
public getTimeZones(): ng.IPromise<blocks.TimeZone[]> {
return this.timeZonesRestangular.getList()
.then((restangularizedTimeZones: restangular.ICollection) => {
return restangularizedTimeZones.plain();
}, (restangularError: any) => {
throw "Error retrieving time zones. Status: " + restangularError.status;
});
}
Now everthing seems to be fine and the response is, indeed, a promise of TimeZone[]

Resources