I didn't find a solution for this problem. I'm currently working with the CRUD Master-Detail Application WebIDE template and added some custom functions with OData calls. When running the app with mock server it loads the mock data. So far so good. But if I send a read request to the mock server it throws a 404 not found error.
Request URL
https://webidetesting[...].dispatcher.hana.ondemand.com/here/goes/your/serviceurl/MyEntity(12345)
Here's the mock server part in my index file flpSandboxMockServer.html:
<script>
sap.ui.getCore().attachInit(function() {
sap.ui.require([
"my/project/localService/mockserver"
], function (mockserver) {
// set up test service for local testing
mockserver.init();
// initialize the ushell sandbox component
sap.ushell.Container.createRenderer().placeAt("content");
});
});
</script>
The OData read call looks like:
onRemoveMyEntityBtnPress: function () {
let oEntityTable = this.byId("lineItemsList");
let aSelectedItems = oEntityTable.getSelectedItems();
let oModel = this.getModel();
for (let oSelectedItem of aSelectedItems) {
let sBindingPath = oSelectedItem.getBindingContext().getPath();
let sGuid = this._selectGuidFromPath(sBindingPath);
this._loadEntityFromService(sGuid, oModel).then((oData) => {
// Next step: change a property value
}).catch((oError) => {
jQuery.sap.log.error(oError);
});
}
if (oModel.hasPendingChanges()) {
oModel.submitChanges();
}
},
_loadEntityFromService: function (sGuid, oModel) {
return new Promise((resolve, reject) => {
oModel.read(`/MyEntity(${sGuid})`, {
success: (oData) => {
resolve(oData);
},
error: (oError) => { // call always ends up here with 404 error
reject(oError);
}
});
});
},
Does someone have an idea what I else have to do to send my read request to the mock service?
Finally found the solution!
I used the OData entity type to read my entity. I changed the destination to my entity set and now it doesn't throw a 404 error.
Related
I have a React application that calls the Places API through Google's dedicated places library.
The library is imported as such:
<script defer src="https://maps.googleapis.com/maps/api/js?key=<API_KEY>&libraries=places&callback=initPlaces"></script>
The code above is inside /public, in index.html. The initPlaces callback, specified in the URL looks as such:
function initPlaces() {
console.log("Places initialized");
}
To make the actual request, the following code is used:
async function makeGapiRequest() {
const service = new window.google.maps.places.AutocompleteService();
const response = await service.getQueryPredictions({
input: "Verona"
});
console.log(res);
}
For testing purposes, the function is called when the document is clicked:
document.addEventListener("click", () => {
makeGapiRequest();
});
On every request, there is a response coming back. For instance, when the input has the value of Verona, the following response is received, and is only visible in the browser network tab:
{
predictions: [
{
description: "Verona, VR, Italy",
...
},
...
],
status: "OK"
}
Whenever maleGapiRequest is called, even though there is a visible response from the API, the response variable is undefined at the time of logging, and the following error is thrown in the console:
places_impl.js:31 Uncaught TypeError: c is not a function
at places_impl.js:31:207
at Tha.e [as l] (places_impl.js:25:320)
at Object.c [as _sfiq7u] (common.js:97:253)
at VM964 AutocompletionService.GetQueryPredictionsJson:1:28
This code is thrown from the Places library imported in /public/index.html.
Did anyone encounter this error before, or has an idea as to what is the problem? I would like it if the solution was available to me, not the library.
The problem was that I was calling the wrong method. Instead of getQueryPredictions call the getPlacePredictions method. It will return different results, but you can configure it to suite your needs.
Old code:
async function makeGapiRequest() {
const service = new window.google.maps.places.AutocompleteService();
const response = await service.getQueryPredictions({
input: "Verona"
});
console.log(res);
}
New code:
async function makeGapiRequest() {
const service = new window.google.maps.places.AutocompleteService();
const response = await service.getPlacePredictions({
input: "Verona",
types: ["(cities)"]
});
console.log(res);
}
I am very new to Jasmine. I am intending to use it for with vanilla javascript project. The initial configuration was a breeze but I am receiving object not defined error while using spyOn.
I have downloaded the version 3.4.0 Jasmine Release Page and added the files 'as is' to my project. I then have changed jasmine.json file accordingly and see the all the example tests passing. However when try spyOn on a private object, I am getting undefined error,
if (typeof (DCA) == 'undefined') {
DCA = {
__namespace: true
};
}
DCA.Audit = {
//this function needs to be tested
callAuditLogAction: function (parameters) {
//Get an error saying D365 is not defined
D365.API.ExecuteAction("bu_AuditReadAccess", parameters,
function (result) { },
function (error) {
if (error != undefined && error.message != undefined) {
D365.Utility.alertDialog('An error occurred while trying to execute the Action. The response from server is:\n' + error.message);
}
}
);
}
}
and my spec class
describe('Audit', function(){
var audit;
beforeEach(function(){
audit = DCA.Audit;
})
describe('When calling Audit log function', function(){
beforeEach(function(){
})
it('Should call Execute Action', function(){
var D365 = {
API : {
ExecuteAction : function(){
console.log('called');
}
}
}
// expectation is console log with say hello
spyOn(D365.API, 'ExecuteAction').and.callFake(() => console.log('hello'));
var params = audit.constructActionParameters("logicalName", "someId", 'someId');
audit.callAuditLogAction(params);
})
})
})
As you can see my spec class does not know about actual D365 object. I was hoping to stub the D365 object without having to inject it. Do I need to stub out whole 365 library and link it to my test runner html?
I got it working after some pondering. So the library containing D365 should still need to be added to my test runner html file. after that I can fake the method call like below,
it('Should call Execute Action', function(){
spyOn(D365.API, 'ExecuteAction').and.callFake(() => console.log('hello'));
var params = audit.constructActionParameters("logicalName", "someId", 'someId');
audit.callAuditLogAction(params);
})
it is now working.
I am using strapi to create APIs.
I want to implement my own Registration API and Login API.
I checked the documentation of strapi but i am not finding any custom API for this.
can any one help me on this?
Same answer, but in more detail:
Strapi creates an Auth controller automatically for you and you can overwrite its behavior.
Copy the function(s) you need (e.g. register) from this file:
node_modules/strapi-plugin-users-permissions/controllers/Auth.js
to:
your_project_root/extensions/users-permissions/controllers/Auth.js
Now you can overwrite the behavior, e.g. pass a custom field inside the registration process {"myCustomField": "hello world"} and log it to the console:
async register(ctx) {
...
...
// log the custom field
console.log(params.myCustomField)
// do something with it, e.g. check whether the value already exists
// in another content type
const itExists = await strapi.query('some-content-type').findOne({
fieldName: params.myCustomField
});
if (!itExists) {
return ctx.badRequest(...)
} else {
console.log('check success')
}
}
Actually, strapi creates an Auth controller to handle these requests. You can just change them to fit in your need.
The path to the controller is:
plugins/users-permissions/controllers/Auth.js
in order to create custom users-permissons apis on server side you have to create
src/extensions/users-permissions/strapi-server.js
and in that file can write or override existing user-permissions plugin apis
here is the example for users/me
const _ = require('lodash');
module.exports = (plugin) => {
const getController = name => {
return strapi.plugins['users-permissions'].controller(name);
};
// Create the new controller
plugin.controllers.user.me = async (ctx) => {
const user = ctx.state.user;
// User has to be logged in to update themselves
if (!user) {
return ctx.unauthorized();
}
console.log('calling about meeeeeeeeeee------')
return;
};
// Add the custom route
plugin.routes['content-api'].routes.unshift({
method: 'GET',
path: '/users/me',
handler: 'user.me',
config: {
prefix: '',
}
});
return plugin;
};
Firstly, I am not a seasoned JS Developer, so please excuse obvious mistakes that I could have made.
I am trying to implement a custom Authenticator for authenticating a user with Keycloak using the OAuth2 Password Grant which requires the client_id be passed as part of the request body.
import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant';
export default OAuth2PasswordGrant.extend({
serverTokenEndpoint: 'http://localhost:8080/something/token',
makeRequest(url, data, headers = {}) {
data.client_id = 'my-app';
return this._super(url, data, headers);
}
});
I have a controller that uses this Authenticator by calling this action:
actions: {
authenticate() {
let {username, password} = this.getProperties('username', 'password');
this.get('session').authenticate('authenticator:oauth2', username, password).then(() => {
// Do something
}).catch((response) => {
// Show error
});
}
}
This causes Firefox to hang and gives me an unresponsive script message.
If I remove the return from the makeRequest() method, I can see from the browser debugger that the call to Keycloak actually returns correctly with the object that contains my token etc. However ember inspector shows some errors related to unresolved promises. But I guess that's because I'm no longer returning the promise.
What am I doing wrong here?
How can I fix the unresponsive script issue?
Is there another way for me to achieve my goal?
Edit 1: This is when I remove the return
Here is the actual object that is returned:
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJTRUNSd09fMlZWdGhxUVBUWnFxNHlqX0tKekxnOElSTjBrQkx5UTlacklrIn0.eyJqdGkiOiI1NDgzZDdkMi0zMDdhLTQyZjItYWUxZC0xYTZjMTZjOTM2ZjAiLCJleHAiOjE1MDgzMzE5MjAsIm5iZiI6MCwiaWF0IjoxNTA4MzMxNjIwLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvc2Z4LWl0cmFuc2Zlci13ZWItYWdlbnQiLCJhdWQiOiJhZ2VudC13ZWItYXBwIiwic3ViIjoiMzZiMWY4OWMtNGYwMC00OTU1LWE0YzMtZWQ0NzZmZDU2OGM3IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWdlbnQtd2ViLWFwcCIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjQwODMxZWFhLTRmMmEtNDk2ZS05NDVkLTdiZWIxN2U0NmU0NCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDo0MjAwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJ1bWFfYXV0aG9yaXphdGlvbiIsImJhY2stb2ZmaWNlLWFnZW50Il19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwibmFtZSI6IlVtYXIgS2hvbHZhZGlhIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidW1hciIsImdpdmVuX25hbWUiOiJVbWFyIiwiZmFtaWx5X25hbWUiOiJLaG9sdmFkaWEiLCJlbWFpbCI6InVtYXJAYWlydmFudGFnZS5jby56YSJ9.eUJFklRiRjQPOC1rQLcqrljsSWmGXCpNNKqLJGAcvbnbwx8X0T1iqrmpFdyMN3EKRrIfTZyYRfcTEbpcBEjZcZtgDY9V0Ntvt4pvpUx_8Ey6I8xZQolHVwferjM30puLqG8MImADUimNrj3ghbJbAaCOJktIKgLnTIhDbkNb-8lzgbyq-rEP6lYAWjQ2OuOZnc8NQQ9CJiR9M1SB79SEmY2iQW9E_J8xo8BgZQ0GUBrhaWPo-Kn4RnlEcRNzVnlLHQKi5FM7Zpov3SMQUbAeLat38V41y09ap2XVCy7MfL_7-TrSlMx0TLrhWqPgA5aaXbmsT9_vKOoXNZoJ9bWCuA",
"expires_in":300,
"refresh_expires_in":1800,
"refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJTRUNSd09fMlZWdGhxUVBUWnFxNHlqX0tKekxnOElSTjBrQkx5UTlacklrIn0.eyJqdGkiOiIxNTUwNDIyZS02OThkLTQ5N2ItODZmYi00YmY5MTFlMTcwYzYiLCJleHAiOjE1MDgzMzM0MjAsIm5iZiI6MCwiaWF0IjoxNTA4MzMxNjIwLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvc2Z4LWl0cmFuc2Zlci13ZWItYWdlbnQiLCJhdWQiOiJhZ2VudC13ZWItYXBwIiwic3ViIjoiMzZiMWY4OWMtNGYwMC00OTU1LWE0YzMtZWQ0NzZmZDU2OGM3IiwidHlwIjoiUmVmcmVzaCIsImF6cCI6ImFnZW50LXdlYi1hcHAiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiI0MDgzMWVhYS00ZjJhLTQ5NmUtOTQ1ZC03YmViMTdlNDZlNDQiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsidW1hX2F1dGhvcml6YXRpb24iLCJiYWNrLW9mZmljZS1hZ2VudCJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX19.XgYSZWwfaHeY1yZZuwnQ5bj-0IHP4UEmiPTqaeCE1KVyjl3kZz3HJVisndtcKPr05kalS-M_NqU0TaYvbcZ_zesJRIga5sz4gGRqObUmUCUJoQ_iWoOhbM2SutiVnlfgJDACvOxegIcSvakZTgQsEcSweio_0kMFqi-2DYzFp6Rl0TpQ8vALLkc7rEOonUGyt7S4qQzkT-xB1_ZDlSVfm6mC-QKYNZhtqBT18P7MKxBhEgwrJtCytA_4ft7qNAbgvZ3kUohcbhzxGvtHej5RKHNI2wTzwK3IWHbkLWNndxSk_Lzj2-lCx380VpTkVpiDJfq5umjskOmI13dyPF7paA",
"token_type":"bearer",
"not-before-policy":0,
"session_state":"40831eaa-4f2a-496e-945d-7beb17e46e44"
}
This is what ember inspector (Promises) shows:
Here is the stacktrace from the Promise:
Ember Inspector ($E): authenticate/<#http://localhost:4200/assets/vendor.js:77927:9
initializePromise#http://localhost:4200/assets/vendor.js:63591:7
Promise#http://localhost:4200/assets/vendor.js:64067:35
authenticate#http://localhost:4200/assets/vendor.js:77919:14
authenticate#http://localhost:4200/assets/vendor.js:78528:14
authenticate#http://localhost:4200/assets/vendor.js:79420:14
authenticate#http://localhost:4200/assets/sfx-itransfer-web-agent.js:855:9
join#http://localhost:4200/assets/vendor.js:20249:24
run$1.join#http://localhost:4200/assets/vendor.js:37657:12
makeClosureAction/</<#http://localhost:4200/assets/vendor.js:29073:16
exports.flaggedInstrument#http://localhost:4200/assets/vendor.js:37087:14
makeClosureAction/<#http://localhost:4200/assets/vendor.js:29072:15
submit/<#http://localhost:4200/assets/vendor.js:70453:20
tryCatch#http://localhost:4200/assets/vendor.js:63549:14
invokeCallback#http://localhost:4200/assets/vendor.js:63562:15
publish#http://localhost:4200/assets/vendor.js:63532:9
#http://localhost:4200/assets/vendor.js:54458:16
invoke#http://localhost:4200/assets/vendor.js:19948:17
flush#http://localhost:4200/assets/vendor.js:19827:25
flush#http://localhost:4200/assets/vendor.js:20019:25
end#http://localhost:4200/assets/vendor.js:20128:26
run#http://localhost:4200/assets/vendor.js:20212:21
join#http://localhost:4200/assets/vendor.js:20219:24
run$1.join#http://localhost:4200/assets/vendor.js:37657:12
handleEvent/<#http://localhost:4200/assets/vendor.js:58233:18
exports.flaggedInstrument#http://localhost:4200/assets/vendor.js:37087:14
handleEvent#http://localhost:4200/assets/vendor.js:58232:17
_Mixin$create.handleEvent#http://localhost:4200/assets/vendor.js:57385:12
_bubbleEvent#http://localhost:4200/assets/vendor.js:57685:14
setupHandler/<#http://localhost:4200/assets/vendor.js:57619:20
dispatch#http://localhost:4200/assets/vendor.js:5546:16
add/elemData.handle#http://localhost:4200/assets/vendor.js:5355:6
Actually you solution looks like right.
I guess you have a problem in server response or mismatch request methods GET/POST. To solve this try to debug promise inside makeRequest.
return new RSVP.Promise((resolve, reject) => {
fetch(url, options).then((response) => {
response.text().then((text) => { //<-- here debug text
let json = text ? JSON.parse(text) : {};
if (!response.ok) { //<-- and here debug response
response.responseJSON = json;
reject(response);
} else {
resolve(json);
}
});
}).catch(reject);
So if problem will here, just rewrite whole method of makeRequest and add you own promise with custom fetch.
Another way is to write custom Authenticator, overriding authenticate, restore and (optionally) invalidate methods as wrote in documentation: https://github.com/simplabs/ember-simple-auth#implementing-a-custom-authenticator
In my express app, when the DELETE method below is called, the GET method is immediately called after and it's giving me an error in my angular code that says it is expected an object but got an array.
Why is my GET method being called when i'm explicitly doing res.send(204); in my DELETE method and how can I fix this?
Server console:
DELETE /notes/5357ff1d91340db03d000001 204 4ms
GET /notes 200 2ms - 2b
Express Note route
exports.get = function (db) {
return function (req, res) {
var collection = db.get('notes');
collection.find({}, {}, function (e, docs) {
res.send(docs);
});
};
};
exports.delete = function(db) {
return function(req, res) {
var note_id = req.params.id;
var collection = db.get('notes');
collection.remove(
{ _id: note_id },
function(err, doc) {
// If it failed, return error
if (err) {
res.send("There was a problem deleting that note from the database.");
} else {
console.log('were in delete success');
res.send(204);
}
}
);
}
}
app.js
var note = require('./routes/note.js');
app.get('/notes', note.get(db));
app.post('/notes', note.create(db));
app.put('/notes/:id', note.update(db));
app.delete('/notes/:id', note.delete(db));
angularjs controller
$scope.delete = function(note_id) {
var note = noteService.get();
note.$delete({id: note_id});
}
angularjs noteService
angular.module('express_example').factory('noteService',function($resource, SETTINGS) {
return $resource(SETTINGS.base + '/notes/:id', { id: '#id' },
{
//query: { method: 'GET', isArray: true },
//create: { method: 'POST', isArray: true },
update: { method: 'PUT' }
//delete: { method: 'DELETE', isArray: true }
});
});
** UPDATE **
To help paint the picture, here's the angular error i'm getting:
Error: [$resource:badcfg] Error in resource configuration. Expected response to contain an object but got an array http://errors.angularjs.org/1.2.16/$resource/badcfg?p0=object&p1=array
I'm assuming that i'm getting this error because my delete method is calling my get method (somehow) and the get method returns the entire collection.
Server side
You're removing an element from a collection in your delete function. This is done asynchronously and calling your callback when it's finished.
During this time, other requests are executed, this is why your GET request is executed before your DELETE request is finished.
The same happens in your get function, you're trying to find an element from a collection and this function is too asynchronous.
But this is server side only and it is fine, it should work this way, your problem is located client side.
Client side
If you want to delete your note after you got it, you will have to use a callback function in your angular controller which will be called only when you got your note (if you need help on that, show us your noteService angular code).
This is some basic javascript understanding problem, actions are often made asynchronously and you need callbacks to have an execution chain.
Maybe try doing something like this:
$scope.delete = function(note_id) {
var note = noteService.get({ id: note_id }, function()
{
note.$delete();
});
}
Your code doesn't make sense though, why is there a get in the $scope.delete? Why not do as simply as following:
$scope.delete = function(note_id) {
noteService.delete({ id: note_id });
}
Error
I think you get this error because of what your server sends in your exports.delete function. You're sending a string or no content at all when angular expects an object (a REST API never sends strings). You should send something like that:
res.send({
results: [],
errors: [
"Your error"
]
});