Where can i find the annotations for the api.order.search controller? - shopware6

I need to write a new api endoint, very similar to the api/search/order endpoint using a custom implementation. Basically i want to add a new response format for another system.
This will also be a new route like api/custom/search/order.
My approach is that i want to write a new controller by extending the existing controller, defined as follows:
+--------------+---------------------------------------------------------------------------------------+
| Property | Value |
+--------------+---------------------------------------------------------------------------------------+
| Route Name | api.order.search |
| Path | /api/search/order{path} |
| Path Regex | {^/api/search/order(?P<path>(?:\/[0-9a-f]{32}\/(?:extensions\/)?[a-zA-Z-]+)*\/?)$}sDu |
| Host | ANY |
| Host Regex | |
| Scheme | ANY |
| Method | POST |
| Requirements | path: (\/[0-9a-f]{32}\/(extensions\/)?[a-zA-Z-]+)*\/? |
| | version: \d+ |
| Class | Symfony\Component\Routing\Route |
| Defaults | _controller: Shopware\Core\Framework\Api\Controller\ApiController::search() |
| | _routeScope: array (0 => 'api',) |
| | entityName: order |
| Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
| | utf8: true |
+--------------+---------------------------------------------------------------------------------------+
From my current setup i was able to write a new controller but the controller can not be found in the routes as there is no annotation in the original one. I could add a custom annotation but i was looking at the original definition and actually there is no annotation.
public function search(Request $request, Context $context, ResponseFactoryInterface $responseFactory, string $entityName, string $path): Response
{
[$criteria, $repository] = $this->resolveSearch($request, $context, $entityName, $path);
$result = $context->scope(Context::CRUD_API_SCOPE, function (Context $context) use ($repository, $criteria): EntitySearchResult {
return $repository->search($criteria, $context);
});
$definition = $this->getDefinitionOfPath($entityName, $path, $context);
return $responseFactory->createListingResponse($criteria, $result, $definition, $request, $context);
}
My question is: Is there something like a dynamic annotation which will automatically be created? I can not find any information about how the path for /api/search/order{path} is defined.
/**
* #Route(defaults={"_routeScope"={"api"}})
*/
class OrderActionController extends ApiController
{
public function search(Request $request, Context $context, ResponseFactoryInterface $responseFactory, string $entityName, string $path): Response
{
}
}

I did figure it out. The annotations for the api.search.order are defined in the class Shopware\Core\System\CustomEntity\Api\CustomEntityApiController.
Shopware uses a dynamic annotation which will be used to create the routes. I was looking for a specific annotation and therefore was not able to find it.
#Route(
"/api/search/custom-entity-{entityName}{path}",
name="api.custom_entity_entity.search",
requirements={"path"="(\/[0-9a-f]{32}\/(extensions\/)?[a-zA-Z-]+)*\/?$"},
methods={"POST"}
)

Related

Use map object in karate framework

I am trying to create a scenario where:
Scenario Outline: Create a request
Given print 'reason=<reason>, detail=<detail>, metainfo=<metainfo>'
When call create_request
Then match response.message == "#notnull"
* call json_to_proto request
* print 'response \n', response
Examples:
reason | detail | metainfo
test | Testing | { foo: bar }
My concern is metainfo is defined as a map, "metainfo": "#(karate.get('metainfo', {}))" how do I set values for it as the current logic gives me error: org.graalvm.polyglot.PolyglotException: Expect a map object but found...
Please read this section of the docs: https://github.com/karatelabs/karate#scenario-outline-enhancements
You can use JSON like this. And note that you don't need the <foo> placeholder system. Normal variables work:
Scenario Outline: ${payload.foo}
* match payload == { foo: 'bar' }
Examples:
| payload! |
| { foo: 'bar' } |

Got an error while executing query {"statusCode":500,"error":"Internal Server Error","message":"syntax error at or near \")\""}

I have a db with a primary key address
| Address | other values...|
| ----------------------| -------------- |
| ed.nl | null |
| spelletjes.nl | null |
| kinderspelletjes.nl | null |
I want to write a request that would delete rows that match the address that I pass in the array.
I have this in my missing-urls.js file
async function deleteMissingUrls(request, reply) {
try {
const addresses = request.body;
const { rows } = await pg.query(formatQry.deleteByAddress, [addresses]);
reply.send({
total: rows.length,
items: rows,
});
} catch (err) {
log.error(`Error while deleting. ${err.message}`);
reply.internalServerError(err.message);
}
}
and use this query in my query/missing-urls.js file
export const deleteByAddress = 'DELETE FROM metrics.missing_url WHERE address IN (?)';
When I run this request
curl -X DELETE -d '["ed.nl", "spelletjes.nl"]' -H "Content-Type: application/json" http://localhost:3000/api/missing/urls
I got an error.
{"statusCode":500,"error":"Internal Server Error","message":"syntax error at or near \")\""}%
What I am doing wrong?
You should use any operator column = any(?)
If you use node-postgres driver your code look like this:
await pg.quert('DELETE FROM metrics.missing_url WHERE address = ANY($1)', [['address 1', 'address 2', ...]])

Using karate-config parameters in a feature file

The karate header examples do not show how to access config values other than baseUrl. When I switch environments (passing in -Dkarate.env=qual as part of the run command) then baseUrl is set correctly.
The problem is, I want to use other config values as shown here but when I run the test, it fails to access config.ApiKey correctly. Instead I get this error
html report:
file:/C:/bitbucket/karate-checkdigit-api/target/surefire-reports/TEST-features.checkdigitapi.VA.html
Tests run: 250, Failures: 0, Errors: 50, Skipped: 175, Time elapsed: 4.112 sec <<< FAILURE!
* def secretKey = config.apiKey(| XYZ | 2110974841 | 204 | Valid |) Time elapsed: 0.005 sec <<< ERROR!
java.lang.RuntimeException: no variable found with name: config
at com.intuit.karate.Script.getValuebyName(Script.java:323)
at com.intuit.karate.Script.evalJsonPathOnVarByName(Script.java:378)
at com.intuit.karate.Script.eval(Script.java:309)
at com.intuit.karate.Script.eval(Script.java:194)
at com.intuit.karate.Script.assign(Script.java:656)
at com.intuit.karate.Script.assign(Script.java:587)
at com.intuit.karate.StepDefs.def(StepDefs.java:265)
at ✽.* def secretKey = config.apiKey(features/checkdigitapi/XYZ.feature:6)
My .feature file and karate-config.js are below.
XYZ.feature
#regression
Feature: Checkdigit Algorithm API
Background:
* url baseUrl
* def secretKey = config.apiKey
* configure ssl = true
Scenario Outline: Testing XYZ algorithm
* configure headers = { KeyId: secretKey, Accept: 'application/json' }
Given path 'headers'
And param url = baseUrl
And params { customerId: '<custcode>', algoId: '<algo>' }
When method get
Then status <val>
Examples:
| algo | custcode | val | comment |
| XYZ | 2110974841 | 204 | Valid |
| XYZ | 7790011614 | 204 | Valid |
| XYZ | 5580015174 | 204 | Valid |
| XYZ | 2110974840 | 400 | expected check digit 1 |
| XYZ | 211097484 | 400 | not 10 digits |
| XYZ | 211097484x | 400 | not numeric |
karate-config.js
function() {
//set up runtime variables based on environment
//get system property 'karate.env'
var env = karate.env;
if (!env) { env = 'dev'; } // default when karate.env not set
// base config
var config = {
env: env,
baseUrl: 'https://localapi.abc123.example.com/api/v1/validate/customerid',
apiKey: ''
}
//switch environment
if (env == 'dev') {
config.baseUrl = 'https://devapi.abc123.example.com/api/v1/validate/customerid';
config.apiKey = 'fake-1ba403ca8938176f3a62de6d30cfb8e';
}
else if (env == 'qual') { //Pre-production environment settings
config.baseUrl = 'https://qualapi.abc123.example.com/api/v1/validate/customerid';
config.apiKey = 'fake-d5de2eb8c0920537f5488f6535c139f2';
}
karate.log('karate.env =', karate.env);
karate.log('config.baseUrl =', config.baseUrl);
karate.log('config.apiKey =', config.apiKey);
return config;
}
(similar issue here, using a separate headers.js: https://github.com/intuit/karate/issues/94)
Keep in mind that all the keys within the JSON object returned by karate-config.js will be injected as variables, and nothing else. So you will not be able to refer to config, but you will certainly be able to refer to apiKey.
I think if you make this simple change, things will start working:
* def secretKey = apiKey
Also, I think you have a problem in the first line of the scenario, it should be:
* configure headers = { KeyId: '#(secretKey)', Accept: 'application/json' }
FYI my final, correctly working XYZ.feature file looks like this now.
The line Given path 'headers' caused header info to creep into the url so it's removed.
XYZ.feature
#regression
Feature: Checkdigit Algorithm API
Background:
* url baseUrl
* def secretKey = apiKey
* configure ssl = true
Scenario Outline: Testing XYZ algorithm
* configure headers = { KeyId: '#(secretKey)', Accept: 'application/json' }
Given url baseUrl
And params { customerId: '<custcode>', algoId: '<algo>' }
When method get
Then status <val>
Examples:
[...]

NormalizedUserName and NormalizedEmail - ASP.Net Core login

When registering a new user (test#here.com), I get the following saved into the [dbo].[AspNetUsers] table:
| Email | NormalizedEmail | NormalizedUserName | UserName |
+-----------------+------------------+---------------------+-----------------+
| test#here.com | TEST#HERE.COM | TEST#HERE.COM | test#here.com |
However, I thought that the username was not very user friendly in my layout and wanted to change it to something like:
| Email | NormalizedEmail | NormalizedUserName | UserName |
+-----------------+------------------+---------------------+-----------------+
| test#here.com | TEST#HERE.COM | TEST#HERE.COM | John Smith |
On the other hand, I found that by using the ResetPassword in the Account controller or the ChangePassword method in the Manage controller, the NormalizedUserName was overwritten by the UserName to get something like that:
| Email | NormalizedEmail | NormalizedUserName | UserName |
+-----------------+------------------+---------------------+-----------------+
| test#here.com | TEST#HERE.COM | JOHN SMITH | John Smith |
Having something like that prevents me to login now as it is using the NormalizedUserName to get identified which is not an email anymore...
Several questions:
Why, when I reset or change my password, my NormalizedUserName gets overwritten?
Why does it use the NormalizedUserName to get logged? It doesn't make sense to me as we provide an email in the login interface, it should be using the NormalizedEmail instead.
It would be more convenient to make the distinction between user name for display purposes and user email for identification purpose.
Thank you for your help
Sylvain
I suppose you, like me, changed the scaffolded RegisterModel under Areas.Identity.Pages.Account to include UserName, so that UserName is no longer set as Input.Email.
var user = new ApplicationUser { UserName = Input.UserName, Email = Input.Email }
var result = await _userManager.CreateAsync(user, Input.Password);
Login is handled by Login.cshtml.cs and Login.cshtml, which sends Input.Email as the first argument to SignInManager.PasswordSignInAsync(). This method actually expects a UserName, not an Email.
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
I solved this by simply changing the code to look up the correct UserName by Email.
string userName = _userManager.FindByEmailAsync(Input.Email).Result.UserName;
var result = await _signInManager.PasswordSignInAsync(userName, Input.Password, Input.RememberMe, lockoutOnFailure: false);

Test file download with Capybara and Cucumber

I am trying to test a download using Capybara and Cucumber.
The test steps look like:
When(/^I export to CSV$/) do
export_button = find("div.results-table input[type=\"submit\"]")
export_button.click
end
Then(/^I should be prompted to download the CSV$/) do
Capybara.current_driver = :webkit #switch driver so we can see the response_headers
page.response_headers['Content-Disposition'].should include("filename=\"transactions\"")
end
I had to add the capybara webkit driver in the middle of the test so that I can use the response_headers, but the response_headers seem to return an empty object. I am using the default driver before that because I need to click the button, but I wonder if switching drivers like this is why I am not getting anything in the response_header?
I have done it in my recent project as shown below
Given Download folder for export is empty
And Joe clicks "Download Csv" button
Then The contents of the downloaded csv should be:
|TEAM_ID | TEAM_EXTERNAL_ID | TYPE | REG_OPTION | TEAM_REG_BRACKET | RACE_NAME | WAVE | BIB | BRACKET | STATUS | TEAM_NAME | TEAM_TYPE | ATHLETE_COUNT | MIN_MEMBERS | MAX_MEMBERS | TEAM_MEMBERS |
| 8 | | TEAM | 10k Aggregate | N/A | 10k | Universal | 208 | Overall | DNF | Team 08 | Aggregate |0 | | | |
And the capybara for this cucumber will be like
Given(/^Download folder for export is empty$/) do
FileUtils.rm_rf('/home/vagrant/Downloads/')
end
And(/^(\S*) clicks "([^"]*)" button$/) do |user, arg|
button = find_button(arg)
button.click
end
And /^The contents of the downloaded csv should be:$/ do |table|
for i in 0..5
if(Dir.glob('/home/vagrant/Downloads/*.csv'))
break;
end
sleep(1); // if not found wait one second before continue looping
end
if(Dir.glob('/home/vagrant/Downloads/*.csv'))
Dir['/home/vagrant/Downloads/*'].each do |file_name|
arr = CSV.read(file_name, :col_sep => "\t", :row_sep => "\r\n", encoding: "UTF-16:UTF-8")
table.raw[0...table.raw.length].each_with_index do |row, row_index|
row.each_with_index do |value, index|
if arr[row_index][index] == nil
arr[row_index][index] = ""
end
if index == 1
else
puts "#{index}" + 'Value in table = ' + "#{value} + 'Value in file' + #{arr[row_index][index]}"
value.should eq arr[row_index][index]
end
end
end
end
else
puts "/home/vagrant/Downloads/*.csv file not found!"
end
end
Hope this resolves you problem for downloading CSV and verifying its contents too :)