MyBatis - some records are missing in result - mapping

I have a result map like:
<resultMap type="cz.pse.agata.commons.dto.Security" id="detailed_security">
<result property="id" column="VAID"/>
<result property="issueId" column="VAIDVA"/>
<result property="effectiveDate" column="VAEFDT"/>
<result property="bic" column="VABIC"/>
.
.
.
<association property="isin" column="VACVAL" javaType="cz.pse.agata.commons.dto.ISIN">
<result property="isin" column="VACVAL"/>
</association>
<association property="legalPerson" javaType="cz.pse.agata.commons.dto.LegalPerson"
resultMap="legalPerson" columnPrefix="specialist_person_"/>
</association>
<association property="emitent" column="VAEMIT" javaType="cz.pse.agata.commons.dto.LegalPerson"
resultMap="cz.pse.agata.commons.dto.mapper.LegalPersonMapper.legalPerson"/>
<association property="administrator" column="VAIDAD" javaType="cz.pse.agata.commons.dto.LegalPerson"
resultMap="legalPerson"/>
<association property="changeReason" column="VAIDCD" javaType="cz.pse.agata.commons.dto.ChangeReason">
<result property="id" column="CDID"/>
<result property="reason" column="CDTEXT"/>
</association>
<association property="tail" javaType="Object">
<discriminator javaType="String" column="VATVAL">
<case value="000" resultType="cz.pse.agata.commons.dto.SecurityTailImpl">
<result property="issueId" column="VAIDVA"/>
</case>
<case value="520" resultType="cz.pse.agata.commons.dto.SecurityTailImpl">
<result property="issueId" column="VAIDVA"/>
</case>
<case value="100" resultMap="cz.pse.cp.dto.mapper.BondMapper.Bond"/>
<case value="150" resultMap="cz.pse.cp.dto.mapper.BondMapper.Bond"/>
<case value="200" resultMap="cz.pse.cp.dto.mapper.BondMapper.Bond"/>
<case value="500" resultType="cz.pse.agata.commons.dto.SecurityTailImpl">
<result property="issueId" column="VAIDVA"/>
</case>
<case value="510" resultType="cz.pse.agata.commons.dto.SecurityTailImpl">
<result property="issueId" column="VAIDVA"/>
</case>
<case value="610" resultMap="cz.pse.cp.dto.mapper.WarrantMapper.Warrant"/>
<case value="660" resultMap="cz.pse.cp.dto.mapper.WarrantMapper.Warrant">
<result property="id" column="WAID"/>
</case>
<case value="600" resultMap="cz.pse.cp.dto.mapper.InvestementCertificateMapper.InvestementCertificate"/>
<case value="650" resultMap="cz.pse.cp.dto.mapper.InvestementCertificateMapper.InvestementCertificate"/>
<case value="300" resultType="cz.pse.agata.commons.dto.SecurityTailImpl">
<result property="issueId" column="VAIDVA"/>
</case>
<case value="305" resultType="cz.pse.agata.commons.dto.SecurityTailImpl">
<result property="issueId" column="VAIDVA"/>
</case>
<case value="400" resultType="cz.pse.agata.commons.dto.SecurityTailImpl">
<result property="issueId" column="VAIDVA"/>
</case>
</discriminator>
</association>
</resultMap>
And I have a query, that returns 4 rows:
==> Preparing: with dates as ( select vaefdt as dat from ....
==> Parameters: 109735(Long), 109735(Long), ....
<== Total: 4
But the resulting list has 3 items only.
After some debugging I realized, that it happens (probably) because 2 of the 4 rows have all data the same except tail values:
(Method org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForNestedResultMap(ResultSetWrapper, ResultMap, ResultHandler, RowBounds, ResultMapping))
private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
final DefaultResultContext resultContext = new DefaultResultContext();
skipRows(rsw.getResultSet(), rowBounds);
Object rowValue = null;
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
Object partialObject = nestedResultObjects.get(rowKey);
// issue #577 && #542
if (mappedStatement.isResultOrdered()) {
if (partialObject == null && rowValue != null) {
nestedResultObjects.clear();
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);
} else {
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);
if (partialObject == null) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
}
if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
Note bold text above: for the 2nd iteration proper rowValue is created, but it is not added to the result, because partialObject is not null.
Any help would be appreciated.

Related

How do I author Azure API Management policy to allow either a scope or a role

I'd like to restrict an API for a set of AD users using scopes and a set of daemon apps using app roles. However, following Azure APIM policy will check only if both claims are present. How do I rewrite the below policy to allow EITHER a scope OR an app role to be present in the JWT token:
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized">
<required-claims>
<claim name="scp" match="any">
<value>API.Request.Get</value>
</claim>
<claim name="roles" match="any">
<value>API.Request.Get</value>
</claim>
</required-claims>
</validate-jwt>
My idea is combine choose when and jwt-validate, here's my policy, it can choose to validate scp or roles, but I don't know why it can't correctly validate the value, I'm really not an expert in apim.
<inbound>
<base />
<set-variable name="isScp" value="#{
string isScp = "false";
string authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
if (authHeader?.Length > 0)
{
string[] authHeaderParts = authHeader.Split(' ');
if (authHeaderParts?.Length == 2 && authHeaderParts[0].Equals("Bearer", StringComparison.InvariantCultureIgnoreCase))
{
Jwt jwt;
if (authHeaderParts[1].TryParseJwt(out jwt))
{
if(jwt.Claims.GetValueOrDefault("scp", "null") != "null"){
isScp = "true";
}
}
}
}
return isScp;
}" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault("isScp") == "false")">
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Server is unavailable for roles">
<required-claims>
<claim name="roles" match="any">
<value>User.ReadWrite.All</value>
</claim>
</required-claims>
</validate-jwt>
</when>
<otherwise>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Server is unavailable for scp">
<required-claims>
<claim name="scp" match="any" separator=" ">
<value>User.ReadWrite.All</value>
</claim>
</required-claims>
</validate-jwt>
</otherwise>
</choose>
</inbound>
By the way, the validation can also write in the code, and not to use validate-jwt, that will be :
<inbound>
<base />
<set-variable name="pass" value="#{
bool isContainScp = false;
bool isContainRoles = false;
string pass = "false";
string authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
if (authHeader?.Length > 0)
{
string[] authHeaderParts = authHeader.Split(' ');
if (authHeaderParts?.Length == 2 && authHeaderParts[0].Equals("Bearer", StringComparison.InvariantCultureIgnoreCase))
{
Jwt jwt;
if (authHeaderParts[1].TryParseJwt(out jwt))
{
string tempScp = jwt.Claims.GetValueOrDefault("scp", "null");
if(tempScp != "null"){
isContainScp = tempScp.Contains("User.ReadWrite.All");
}else{
//write logic here
isContainRoles = true;
}
}
}
}
if(isContainScp || isContainRoles){
pass = "true";
}
return pass;
}" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault("pass") == "false")">
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized hhh" />
</return-response>
</when>
<otherwise />
</choose>
</inbound>
Thanks Tiny Wang for giving me a the sample. authHeader already contains the token. Therefore, there is no need to split it further to get the token. Here is updated one:
<inbound>
<base />
<set-variable name="clientType" value="#{
bool isAuthorized = false;
string authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
if (authHeader?.Length == 0) {
return "No Authorization Header";
}
Jwt jwt;
if (!authHeader.TryParseJwt(out jwt)) {
return "Parse JWT Token failed";
}
bool isPublicClient = jwt.Claims.GetValueOrDefault("azpacr", "null").Equals("0");
bool isClientSecret = jwt.Claims.GetValueOrDefault("azpacr", "null").Equals("1");
bool isClientCertificate = jwt.Claims.GetValueOrDefault("azpacr", "null").Equals("2");
if (isPublicClient) {
return "Public Client";
} else if (isClientSecret) {
return "Client Secret";
} else if (isClientCertificate) {
return "Client Certificate";
}
return "Unauthorized";
}" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault("clientType") == "Public Client")">
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized by APIM policy." require-expiration-time="false">
<openid-config url="https://login.microsoftonline.com/YourTenantHere/v2.0/.well-known/openid-configuration" />
<audiences>
<audience>Your audience here</audience>
</audiences>
<issuers>
<issuer>https://login.microsoftonline.com/YourTenantHere/v2.0</issuer>
</issuers>
<required-claims>
<claim name="scp" match="any">
<value>API.Request.Get</value>
</claim>
</required-claims>
</validate-jwt>
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
</return-response>
</when>
<when condition="#(context.Variables.GetValueOrDefault("clientType") == "Client Secret")">
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized by APIM policy." require-expiration-time="false">
<openid-config url="https://login.microsoftonline.com/your tenant here/v2.0/.well-known/openid-configuration" />
<audiences>
<audience>Your Audience Here</audience>
</audiences>
<issuers>
<issuer>https://login.microsoftonline.com/Your tenant here/v2.0</issuer>
</issuers>
<required-claims>
<claim name="roles" match="any">
<value>App.Request.Get</value>
</claim>
</required-claims>
</validate-jwt>
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
</return-response>
</when>
<otherwise>
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized hhh" />
</return-response>
</otherwise>
</choose>
</inbound>
The high road to acheive the roles claim from JWT will be to add 'output-token-variable-name' prperty in validate-jwt policy.
Here is an example (the required claim here is 'emails' but the same idea):
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid." require-scheme="Bearer" output-token-variable-name="jwt">
<openid-config url="https://csmsorg.b2clogin.com/csmsorg.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1_susi_reset_v2" />
<audiences>
<audience>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx</audience>
</audiences>
<issuers>
<issuer>https://xxxxxx.b2clogin.com/e748014c-129c-4d13-accc-e5e1bb7c38eb/v2.0/</issuer>
</issuers>
<required-claims>
<claim name="tfp" match="any">
<value>B2C_1_susi_reset_v2</value>
<value>B2C_1__SUSI</value>
</claim>
</required-claims>
</validate-jwt>
<choose>
<when condition="#(!((Jwt)context.Variables["jwt"]).Claims["emails"].Contains("xxxxxx#gmail.com"))">
<return-response>
<set-status code="403" reason="Forbidden" />
</return-response>
</when>
</choose>
</inbound>

react navigation v6 and v5, Disable swipe back action

I'm working with react navigation 5 :
I created MainStackScreen and AuthStackScreen,
const AuthStack = createStackNavigator();
function AuthStackScreen() {
return (
<AuthStack.Navigator headerMode="none">
<AuthStack.Screen name="Main" component={Main} />
<AuthStack.Screen name="Login" component={Login} />
<AuthStack.Screen name="Registration" component={Registration} />
<AuthStack.Screen name="Home" component={DrawerNavigator} />
<AuthStack.Screen name="Notifications" component={Notifications} />
<AuthStack.Screen name="SmsValidation" component={SmsValidation} />
<AuthStack.Screen name="MoreRegistrationInfo" component={MoreRegistrationInfo} />
</AuthStack.Navigator>
);
}
MainStackScreen :
const MainScreen = createStackNavigator();
function MainStackScreen({navigation}) {
return (
<MainScreen.Navigator>
<MainScreen.Screen name="Main" component={Main} />
<MainScreen.Screen name="SmsValidation" component={SmsValidation} />
<MainScreen.Screen name="MoreRegistrationInfo" component={MoreRegistrationInfo} />
</MainScreen.Navigator>
);
}
I want to prevent IOS swipe action back between Login my screens
You can set gestureEnabled to false in a screen like:
<AuthStack.Screen
name="Login"
component={Login}
options={{gestureEnabled: false}}
/>
Or the whole navigator like:
<AuthStack.Navigator screenOptions={{gestureEnabled: false}}>
...
</AuthStack.Navigator>
/* -------------------------------------------------------------------------- */
/* AUTH STACK */
/* -------------------------------------------------------------------------- */
const AuthStack = createStackNavigator();
function AuthStackScreen() {
return (
<AuthStack.Navigator headerMode="none">
<AuthStack.Screen name="Main" component={Main} />
<AuthStack.Screen name="Login" component={Login} options={{gestureEnabled: false}} />
<AuthStack.Screen
name="Registration"
component={Registration}
options={{gestureEnabled: false}}
/>
<AuthStack.Screen name="Home" component={DrawerNavigator} options={{gestureEnabled: false}} />
<AuthStack.Screen
name="Notifications"
component={Notifications}
options={{gestureEnabled: false}}
/>
<AuthStack.Screen
name="SmsValidation"
component={SmsValidation}
options={{gestureEnabled: false}}
/>
<AuthStack.Screen
name="MoreRegistrationInfo"
component={MoreRegistrationInfo}
options={{gestureEnabled: false}}
/>
</AuthStack.Navigator>
);
}
You can set a beforeRemove listener to prevent going back.
React Navigation Docs

SharePoint Online BCS OData External Content Can't Update, View or Delete

Using Visual Studio 2013, I created an Entity Model over an existing database. Each table has a GUID for it's primary key. I created an MVC Web API project with related OData Bindings and Controllers.
Here is how I create the OData Binding;
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<HRPosition>("HRPositions").EntityType.HasKey(p=>p.HTPositionGuid);
Here is a sample controller for the HRPositions Entity.
public class HRPositionsController : ODataController
{
private EFSEntities db = new EFSEntities();
// GET: odata/HRPositions
[EnableQuery]
public IQueryable<HRPosition> GetHRPositions()
{
return db.HRPositions;
}
// GET: odata/HRPositions(5)
[EnableQuery]
public SingleResult<HRPosition> GetHRPosition([FromODataUri] Guid key)
{
return SingleResult.Create(db.HRPositions.Where(hRPosition => hRPosition.HTPositionGuid == key));
}
// PUT: odata/HRPositions(5)
public async Task<IHttpActionResult> Put([FromODataUri] Guid key, Delta<HRPosition> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
HRPosition hRPosition = await db.HRPositions.FindAsync(key);
if (hRPosition == null)
{
return NotFound();
}
patch.Put(hRPosition);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!HRPositionExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(hRPosition);
}
// POST: odata/HRPositions
public async Task<IHttpActionResult> Post(HRPosition hRPosition)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.HRPositions.Add(hRPosition);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (HRPositionExists(hRPosition.HTPositionGuid))
{
return Conflict();
}
else
{
throw;
}
}
return Created(hRPosition);
}
// PATCH: odata/HRPositions(5)
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] Guid key, Delta<HRPosition> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
HRPosition hRPosition = await db.HRPositions.FindAsync(key);
if (hRPosition == null)
{
return NotFound();
}
patch.Patch(hRPosition);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!HRPositionExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(hRPosition);
}
// DELETE: odata/HRPositions(5)
public async Task<IHttpActionResult> Delete([FromODataUri] Guid key)
{
HRPosition hRPosition = await db.HRPositions.FindAsync(key);
if (hRPosition == null)
{
return NotFound();
}
db.HRPositions.Remove(hRPosition);
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool HRPositionExists(Guid key)
{
return db.HRPositions.Count(e => e.HTPositionGuid == key) > 0;
}
}
Once the OData Service is deployed and using Fiddler I am able to query the Service endpoints and retrieve the full list of data as well as single entity data.
I then created a SharePoint App in which I create an External Content Type by referencing the OData service. This creates the ECT Model definitions for each endpoint.
Here is the ECT for HRPositions;
<?xml version="1.0" encoding="utf-16"?>
<Model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Name="EFSData" xmlns="http://schemas.microsoft.com/windows/2007/BusinessDataCatalog">
<LobSystems>
<LobSystem Name="EFSODATA" Type="OData">
<Properties>
<Property Name="ODataServiceMetadataUrl" Type="System.String">https://efsodataapi.azurewebsites.net/OData/$metadata</Property>
<Property Name="ODataServiceMetadataAuthenticationMode" Type="System.String">PassThrough</Property>
<Property Name="ODataServicesVersion" Type="System.String">2.0</Property>
</Properties>
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
<LobSystemInstances>
<LobSystemInstance Name="EFSODATA">
<Properties>
<Property Name="ODataServiceUrl" Type="System.String">https://efsodataapi.azurewebsites.net/OData</Property>
<Property Name="ODataServiceAuthenticationMode" Type="System.String">PassThrough</Property>
<Property Name="ODataFormat" Type="System.String">application/atom+xml</Property>
<Property Name="HttpHeaderSetAcceptLanguage" Type="System.Boolean">true</Property>
</Properties>
</LobSystemInstance>
</LobSystemInstances>
<Entities>
<Entity Name="HRPositions" DefaultDisplayName="HRPositions" Namespace="EFSData" Version="1.0.0.0" EstimatedInstanceCount="2000">
<Properties>
<Property Name="ExcludeFromOfflineClientForList" Type="System.String">False</Property>
</Properties>
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
<Identifiers>
<Identifier Name="HTPositionGuid" TypeName="System.Guid" />
</Identifiers>
<Methods>
<Method Name="CreateHRPosition" DefaultDisplayName="Create HRPosition" IsStatic="false">
<Properties>
<Property Name="ODataEntityUrl" Type="System.String">/HRPositions</Property>
</Properties>
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
<Parameters>
<Parameter Name="#HTPositionGuid" Direction="In">
<TypeDescriptor Name="HTPositionGuid" DefaultDisplayName="HTPositionGuid" TypeName="System.Guid" IdentifierName="HTPositionGuid" CreatorField="true" />
</Parameter>
<Parameter Name="#PosistionCode" Direction="In">
<TypeDescriptor Name="PosistionCode" DefaultDisplayName="PosistionCode" TypeName="System.String" CreatorField="true" />
</Parameter>
<Parameter Name="#PositionName" Direction="In">
<TypeDescriptor Name="PositionName" DefaultDisplayName="PositionName" TypeName="System.String" CreatorField="true" />
</Parameter>
<Parameter Name="#Description" Direction="In">
<TypeDescriptor Name="Description" DefaultDisplayName="Description" TypeName="System.String" CreatorField="true" />
</Parameter>
<Parameter Name="#CreateHRPosition" Direction="Return">
<TypeDescriptor Name="CreateHRPosition" DefaultDisplayName="CreateHRPosition" TypeName="Microsoft.BusinessData.Runtime.DynamicType">
<TypeDescriptors>
<TypeDescriptor Name="HTPositionGuid" DefaultDisplayName="HTPositionGuid" TypeName="System.Guid" IdentifierName="HTPositionGuid" ReadOnly="true" />
<TypeDescriptor Name="PosistionCode" DefaultDisplayName="PosistionCode" TypeName="System.String" />
<TypeDescriptor Name="PositionName" DefaultDisplayName="PositionName" TypeName="System.String" />
<TypeDescriptor Name="Description" DefaultDisplayName="Description" TypeName="System.String" />
</TypeDescriptors>
</TypeDescriptor>
</Parameter>
</Parameters>
<MethodInstances>
<MethodInstance Name="CreateHRPosition" Type="Creator" ReturnParameterName="#CreateHRPosition" ReturnTypeDescriptorPath="CreateHRPosition">
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
</MethodInstance>
</MethodInstances>
</Method>
<Method Name="ReadSpecificHRPosition" DefaultDisplayName="Read Specific HRPosition" IsStatic="false">
<Properties>
<Property Name="ODataEntityUrl" Type="System.String">/HRPositions(HTPositionGuid=guid'#HTPositionGuid')</Property>
</Properties>
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
<Parameters>
<Parameter Name="#HTPositionGuid" Direction="In">
<TypeDescriptor Name="HTPositionGuid" DefaultDisplayName="HTPositionGuid" TypeName="System.Guid" IdentifierName="HTPositionGuid" />
</Parameter>
<Parameter Name="#HRPosition" Direction="Return">
<TypeDescriptor Name="HRPosition" DefaultDisplayName="HRPosition" TypeName="Microsoft.BusinessData.Runtime.DynamicType">
<TypeDescriptors>
<TypeDescriptor Name="HTPositionGuid" DefaultDisplayName="HTPositionGuid" TypeName="System.Guid" IdentifierName="HTPositionGuid" ReadOnly="true" />
<TypeDescriptor Name="PosistionCode" DefaultDisplayName="PosistionCode" TypeName="System.String" />
<TypeDescriptor Name="PositionName" DefaultDisplayName="PositionName" TypeName="System.String" />
<TypeDescriptor Name="Description" DefaultDisplayName="Description" TypeName="System.String" />
</TypeDescriptors>
</TypeDescriptor>
</Parameter>
</Parameters>
<MethodInstances>
<MethodInstance Name="ReadSpecificHRPosition" Type="SpecificFinder" Default="true" ReturnParameterName="#HRPosition" ReturnTypeDescriptorPath="HRPosition">
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
</MethodInstance>
</MethodInstances>
</Method>
<Method Name="ReadAllHRPosition" DefaultDisplayName="Read All HRPosition" IsStatic="false">
<Properties>
<Property Name="ODataEntityUrl" Type="System.String">/HRPositions?$top=#LimitHRPositionss</Property>
</Properties>
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
<FilterDescriptors>
<FilterDescriptor Name="LimitFilter" DefaultDisplayName="LimitFilter" Type="Limit" />
</FilterDescriptors>
<Parameters>
<Parameter Name="#LimitHRPositionss" Direction="In">
<TypeDescriptor Name="LimitHRPositionss" DefaultDisplayName="LimitHRPositionss" TypeName="System.Int32" AssociatedFilter="LimitFilter">
<Properties>
<Property Name="LogicalOperatorWithPrevious" Type="System.String">None</Property>
<Property Name="Order" Type="System.String">0</Property>
</Properties>
<DefaultValues>
<DefaultValue MethodInstanceName="ReadAllHRPosition" Type="System.Int32">100</DefaultValue>
</DefaultValues>
</TypeDescriptor>
</Parameter>
<Parameter Name="#HRPositions" Direction="Return">
<TypeDescriptor Name="HRPositions" DefaultDisplayName="HRPositions" TypeName="Microsoft.BusinessData.Runtime.IDynamicTypeEnumerator" IsCollection="true">
<TypeDescriptors>
<TypeDescriptor Name="HRPosition" DefaultDisplayName="HRPosition" TypeName="Microsoft.BusinessData.Runtime.DynamicType">
<TypeDescriptors>
<TypeDescriptor Name="HTPositionGuid" DefaultDisplayName="HTPositionGuid" TypeName="System.Guid" IdentifierName="HTPositionGuid" ReadOnly="true" />
<TypeDescriptor Name="PosistionCode" DefaultDisplayName="PosistionCode" TypeName="System.String" />
<TypeDescriptor Name="PositionName" DefaultDisplayName="PositionName" TypeName="System.String" />
<TypeDescriptor Name="Description" DefaultDisplayName="Description" TypeName="System.String" />
</TypeDescriptors>
</TypeDescriptor>
</TypeDescriptors>
</TypeDescriptor>
</Parameter>
</Parameters>
<MethodInstances>
<MethodInstance Name="ReadAllHRPosition" Type="Finder" Default="true" ReturnParameterName="#HRPositions" ReturnTypeDescriptorPath="HRPositions">
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
</MethodInstance>
</MethodInstances>
</Method>
<Method Name="UpdateHRPosition" DefaultDisplayName="Update HRPosition" IsStatic="false">
<Properties>
<Property Name="ODataEntityUrl" Type="System.String">/HRPositions(HTPositionGuid=guid'#HTPositionGuid')</Property>
</Properties>
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
<Parameters>
<Parameter Name="#HTPositionGuid" Direction="In">
<TypeDescriptor Name="HTPositionGuid" DefaultDisplayName="HTPositionGuid" TypeName="System.Guid" IdentifierName="HTPositionGuid" UpdaterField="true" />
</Parameter>
<Parameter Name="#PosistionCode" Direction="In">
<TypeDescriptor Name="PosistionCode" DefaultDisplayName="PosistionCode" TypeName="System.String" UpdaterField="true" />
</Parameter>
<Parameter Name="#PositionName" Direction="In">
<TypeDescriptor Name="PositionName" DefaultDisplayName="PositionName" TypeName="System.String" UpdaterField="true" />
</Parameter>
<Parameter Name="#Description" Direction="In">
<TypeDescriptor Name="Description" DefaultDisplayName="Description" TypeName="System.String" UpdaterField="true" />
</Parameter>
</Parameters>
<MethodInstances>
<MethodInstance Name="UpdateHRPosition" Type="Updater">
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
</MethodInstance>
</MethodInstances>
</Method>
<Method Name="DeleteHRPosition" DefaultDisplayName="Delete HRPosition" IsStatic="false">
<Properties>
<Property Name="ODataEntityUrl" Type="System.String">/HRPositions(HTPositionGuid=guid'#HTPositionGuid')</Property>
</Properties>
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
<Parameters>
<Parameter Name="#HTPositionGuid" Direction="In">
<TypeDescriptor Name="HTPositionGuid" DefaultDisplayName="HTPositionGuid" TypeName="System.Guid" IdentifierName="HTPositionGuid" />
</Parameter>
</Parameters>
<MethodInstances>
<MethodInstance Name="DeleteHRPosition" Type="Deleter">
<AccessControlList>
<AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string">
<Right BdcRight="Edit" />
<Right BdcRight="Execute" />
<Right BdcRight="SelectableInClients" />
<Right BdcRight="SetPermissions" />
</AccessControlEntry>
</AccessControlList>
</MethodInstance>
</MethodInstances>
</Method>
</Methods>
</Entity>
</Entities>
</LobSystem>
</LobSystems>
</Model>
I uploaded the ECT into SharePoint Online BCS and all looks fine;
From there I create an external list and reference the HRPositions ECT, which creates and SP List but is missing the primary key (which is the GUID).
This view shows the proper data;
I am able to add a new item to the list;
And it shows in the read all view;
But I can't edit, delete or view any list item as I get this error for each operation;
I attached to the OData Web Service and could see why the issue is occurring. Turns out the Auto-Generated External Control Types (ECT) within Visual Studio that were reflected off the OData Service have an issue in that for some reason it is formulating the request as /HRPositions(HTPositionGuid=guid'#HTPositionGuid');
It should really only be /HRPositions(guid'#HTPositionGuid');
Can anyone tell me why it's including the HTPositionGuid= within the Parameter list?
I can manually edit the code-generated ECT files for each entity but that seems silly.

Getting 404 error when using dojo.xhrPost + struts?

I am not sure why this was happening. I have a struts form that will post to my struts action's method:
<s:form data-dojo-type="dijit/form/Form" action="SaveRec" method="POST"
enctype="multipart/form-data" theme="simple" id="frmRecord">
<div class="item">
...
Basically, I just hit the link htp://example.com:8080/Test/SaveRec in browser and I will get a workable page, but after using these code, I will get the post 404 error(POST http://example.com:8080/Test/SaveRec.action 404 (Not Found) ):
dojo.connect(dojo.byId('btnSave'), 'onclick', function(event){
// The parameters to pass to xhrPost, the message, and the url to send it to
// Also, how to handle the return and callbacks.
var xhrArgs = {
form: dojo.byId("frmRecord"),
handleAs: "json",
load: function(data){
console.log("Message posted. " + data);
},
error: function(error){
console.log("Message failed to post, " + error);
}
}
console.log("Message being sent...");
// Call the asynchronous xhrPost
var deferred = dojo.xhrPost(xhrArgs);
});
PS: I can run invocation.invoke() and print out message in my interceptor
UPDATE:
OK, I think the problem is the return type of action, I have added the interceptor into the action, however, I still cannot use the getter to retrieve the value:
<struts>
<package name="testJson" extends="json-default">
<action name="SaveRec" class="com.xyz.Test" method="saveRec">
<interceptor-ref name="json">
<param name="contentType">application/json</param>
</interceptor-ref>
<result name="success" type="json">
<param name="root">saveResult</param>
</result>
</action>
...

Transitioning from Scrubyt to Nokogiri- Write to XML or Hash?

I'm trying to transition this bit of code from scrubyt to nokogiri, and am stuck trying to write my results to either a hash or xml. In scrubyt it looks like the following:
require 'rubygems'
require 'scrubyt'
result_data = Scrubyt::Extractor.define do
fetch "http://www.amazon.com/gp/offer-listing/0061673730"
results "//div[#class='resultsset']" do
item "//tbody/tr" do
condition "//div[#class = 'Condition']"
price "//span[#class = 'price']"
shipping "//span[#class = 'price_shipping']"
end
end
end
#description = result_data.to_xml
return #description
end
With nokogiri I can parse out the information I want, but there doesn't seem to be a quick way to return items in a hash or xml document. Here's all I have in nokogiri.
require 'rubygems'
require 'nokogiri'
require 'open-uri'
doc = Nokogiri::HTML(open('http://www.amazon.com/gp/offer-listing/0061673730'))
doc.css('div.condition, span.price, span.price_shipping ').each do |item|
puts item.content
end
How would one return item information to either xml or a hash?
You can use the Builder to build XML.
builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
xml.items {
doc.css('div.condition, span.price, span.price_shipping').each do |o|
xml.item_content = o
end
}
}
end
puts builder.to_xml
Figured it out...
require 'rubygems'
require 'nokogiri'
require 'open-uri'
doc = Nokogiri::HTML(open('http://www.amazon.com/gp/offer-listing/0061673730'))
builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
doc.xpath('//tbody[#class="result"]').each do |res|
xml.result {
res.css('span.price').each do |p|
xml.price = p.content
end
res.css('span.price_shipping').each do |s|
xml.ship = s.content
end
}
end
}
end
puts builder.to_xml
Results:
<?xml version="1.0"?>
<root>
<result>
<price=>$6.09</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$6.48</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$7.12</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$7.31</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$7.52</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$7.52</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$11.53</price=>
</result>
<result>
<price=>$7.56</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$7.61</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$7.61</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$7.95</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$7.95</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$8.59</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$8.99</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$10.05</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$10.32</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$10.32</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$10.55</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$10.56</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$11.42</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$11.59</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$11.90</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$11.95</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$12.07</price=>
<ship=>+ $3.99</ship=>
</result>
<result>
<price=>$12.35</price=>
<ship=>+ $3.99</ship=>
</result>
</root>
Thanks! That's exactly what I need. I'm having trouble looping correctly, though.
require 'rubygems'
require 'nokogiri'
require 'open-uri'
doc = Nokogiri::HTML(open('http://www.amazon.com/gp/offer-listing/0061673730'))
builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
xml.item {
doc.css('span.price').each do |o|
xml.price = o
doc.css('span.price_shipping').each do |o|
end
end
}
}
end
puts builder.to_xml
That returns this:
<?xml version="1.0"?>
<root>
<item>
<price=><span class="price">$6.09</span></price=>
<price=><span class="price">$6.48</span></price=>
<price=><span class="price">$11.95</span></price=>
<ship=><span class="price_shipping">+ $3.99</span></ship=>
<ship=><span class="price_shipping">+ $3.99</span></ship=>
<ship=><span class="price_shipping">+ $3.99</span></ship=>
</item>
</root>
How would I rewrite my code to return something like this:
<?xml version="1.0"?>
<root>
<item>
<price=><span class="price">$6.09</span></price=>
<ship=><span class="price_shipping">+ $3.99</span></ship=>
</item>
<item>
<price=><span class="price">$6.48</span></price=>
<ship=><span class="price_shipping">+ $3.99</span></ship=>
</item>
<item>
<price=><span class="price">$11.95</span></price=>
<ship=><span class="price_shipping">+ $3.99</span></ship=>
</item>
</root>
you may want to omit "=" in
xml.price = p.content

Resources