How to send a query from appsync-simulator to dynamodb-local - amazon-dynamodb

I created a table and seed with dynamodb-local
But I can't get the item from the dynamodb-local table with appsync-simulator
{
"data": null,
"errors": [
{
"message": "Cannot return null for non-nullable field Query.getPerson.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getPerson"
]
}
]
}
Am I misconfiguring serverless.ts?
//dynamodb-local
dynamodb: {
stages: [
"dev",
],
start: {
port: 8000,
inMemory: true,
migrate: true,
seed: true,
},
//seed
seed: {
deb: {
sources: [
{
table: "patients",
sources: ["./src/migrations/patients.json"]
}
]
}
}
},
//appsync-simulator
"appsync-simulator": {
location: ".esbuild",
apiKey: "da2-fakeApiId123456",
watch: false,
dynamoDb: {
endpoint: "http://localhost:8000",
},
},
Modules version
"serverless-appsync-plugin": "^1.13.0",
"serverless-appsync-simulator": "^0.20.0",
"serverless-dynamodb-local": "^0.2.40",
"serverless-offline": "^8.8.0",
"serverless": "^3.0.0",

Related

To fetch a instance name and IP from the gcp_compute_instance_info module

I'm trying to get the list of all instance and its IP, I can able to fetch the instance name as info.resources.0.name, info.resources.1.name, info.resources.2.name etc. Similarly to fetch the network IP I tried info.resources.0.networkInterfaces.networkIP but that didn't work. I need help to rephrase the playbook to use with_items or loop to fetch the list of instances and it's IP. Below is the playbook task:
- name: get info on an instance
gcp_compute_instance_info:
zone: "{{ zone }}"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file }}"
register: info
- debug:
msg: "name - {{ info.resources.0.name }}"
Below is the detailed json output of single instance info:
PLAY [Create an instance] ******************************************************
TASK [get info on an instance] *************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"changed": false,
"failed": false,
"resources": [
{
"canIpForward": false,
"confidentialInstanceConfig": {
"enableConfidentialCompute": false
},
"cpuPlatform": "AMD Rome",
"creationTimestamp": "2022-05-04T23:36:13.606-07:00",
"deletionProtection": false,
"description": "",
"disks": [
{
"autoDelete": true,
"boot": true,
"deviceName": "ansible-automation-platform",
"diskSizeGb": "50",
"guestOsFeatures": [
{
"type": "UEFI_COMPATIBLE"
},
{
"type": "VIRTIO_SCSI_MULTIQUEUE"
},
{
"type": "SEV_CAPABLE"
},
{
"type": "GVNIC"
}
],
"index": 0,
"interface": "SCSI",
"kind": "compute#attachedDisk",
"licenses": [
"https://www.googleapis.com/compute/v1/projects/rhel-cloud/global/licenses/rhel-8-server"
],
"mode": "READ_WRITE",
"source": "https://www.googleapis.com/compute/v1/projects/company_project_prod/zones/us-central1-a/disks/ansible-automation-platform",
"type": "PERSISTENT"
}
],
"displayDevice": {
"enableDisplay": false
},
"fingerprint": "tRhWl0y3JhA=",
"id": "3629531832562667187",
"kind": "compute#instance",
"labelFingerprint": "42WmSpB8rSM=",
"lastStartTimestamp": "2022-05-05T22:09:57.920-07:00",
"lastStopTimestamp": "2022-05-05T07:27:28.617-07:00",
"machineType": "https://www.googleapis.com/compute/v1/projects/company_project_prod/zones/us-central1-a/machineTypes/e2-custom-4-8192",
"metadata": {
"fingerprint": "3tGBZbor1YQ=",
"items": [
{
"key": "ssh-keys",
"value": "user_name:ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEYc1v1Vqgy1tiS1TIDtgNqd1M0Ycp/10q5WBYH8f1E9iXfdZYj6sRSVAzh4C4D0LD925m+mH6MPyr5393vJNcA= google-ssh {\"userName\":\"pugazhendhi.ramakrishnan#wipro.com\",\"expireOn\":\"2022-05-09T06:13:35+0000\"}\nuser_name:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHsNAKOXJCdLjgiYZrlYIQ0NS+dQcBIyxlJXNirKi4zpS4cOTM42FEFyEfByT5s6oexnh/LWLmB8qKGF7hTI7Xup44Nh0W20m2VMHwhGalDELa+KNtrVEg429KutUOC7VHNoSbthj/sVyJ7YiZuRLEPmWlh2VZPLqIvdVgIZ5DhOcAVpeoPEDEVwAMUgceci1d7iyqyVsFA6CcyFTcXqouUgXpjzm6a+6YL4wpSFcZntoVxCA8TFb35ZzXdsSoh1U86GGWRqsntpXHaI5H90z+DpwDg/G8BWSjtZ5vBBgk48n08yg3kLSG5B3mJKtilJhCdmSWqRAqOxpRUvA6kkpf google-ssh {\"userName\":\"pugazhendhi.ramakrishnan#wipro.com\",\"expireOn\":\"2022-05-09T06:13:51+0000\"}"
}
],
"kind": "compute#metadata"
},
"name": "ansible-automation-platform",
"networkInterfaces": [
{
"fingerprint": "WE1S5NA9wnc=",
"kind": "compute#networkInterface",
"name": "nic0",
"network": "https://www.googleapis.com/compute/v1/projects/company_project_prod/global/networks/default",
"networkIP": "10.128.0.17",
"stackType": "IPV4_ONLY",
"subnetwork": "https://www.googleapis.com/compute/v1/projects/company_project_prod/regions/us-central1/subnetworks/default"
}
],
"reservationAffinity": {
"consumeReservationType": "ANY_RESERVATION"
},
"scheduling": {
"automaticRestart": true,
"onHostMaintenance": "MIGRATE",
"preemptible": false,
"provisioningModel": "STANDARD"
},
"selfLink": "https://www.googleapis.com/compute/v1/projects/company_project_prod/zones/us-central1-a/instances/ansible-automation-platform",
"serviceAccounts": [
{
"email": "151315322722-compute#developer.gserviceaccount.com",
"scopes": [
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring.write",
"https://www.googleapis.com/auth/servicecontrol",
"https://www.googleapis.com/auth/service.management.readonly",
"https://www.googleapis.com/auth/trace.append"
]
}
],
"shieldedInstanceConfig": {
"enableIntegrityMonitoring": true,
"enableSecureBoot": false,
"enableVtpm": true
},
"shieldedInstanceIntegrityPolicy": {
"updateAutoLearnPolicy": true
},
"startRestricted": false,
"status": "RUNNING",
"tags": {
"fingerprint": "42WmSpB8rSM="
},
"zone": "https://www.googleapis.com/compute/v1/projects/company_project_prod/zones/us-central1-a"
},
]
}
}
Given the simplified data
info:
changed: false
failed: false
resources:
- canIpForward: false
name: ansible-automation-platform
networkInterfaces:
- name: nic0
networkIP: 10.128.0.17
stackType: IPV4_ONLY
- name: nic1
networkIP: 10.128.0.18
stackType: IPV4_ONLY
- name: nic2
networkIP: 10.128.0.19
stackType: IPV4_ONLY
Iterate the selected names of the resources and combine a dictionary, e.g.
- set_fact:
name_ip: "{{ name_ip|d({})|
combine({item: dict(info.resources|json_query(_query))}) }}"
loop:
- ansible-automation-platform
vars:
_query: "[?name == '{{ item }}'].networkInterfaces[].[name,networkIP]"
gives
name_ip:
ansible-automation-platform:
nic0: 10.128.0.17
nic1: 10.128.0.18
nic2: 10.128.0.19

Why is watcher giving errors?

I want to send slack notifications to a channel as soon as any log with loglevel ERROR appears in my index. I have configured watcher in the following way but it is giving me errors. The slack message must have the log message.
I am not able to configure this exactly.
{
"trigger": {
"schedule": {
"interval": "10s"
}
},
"input": {
"search": {
"request": {
"search_type": "query_then_fetch",
"indices": [
"index-log*",
"index-beat*"
],
"rest_total_hits_as_int": true,
"body": {
"query": {
"match": {
"loglevel": "ERROR"
},
"range": {
"#timestamp": {
"from": "{{ctx.trigger.scheduled_time}}||-5m",
"to": "{{ctx.trigger.triggered_time}}"
}
}
}
}
}
}
},
"condition": {
"compare": {
"ctx.payload.hits.total": {
"gt": 0
}
}
},
"actions": {
"send_trigger": {
"webhook": {
"scheme": "https",
"host": "hooks.slack.com",
"port": 443,
"method": "post",
"path": "/services/XXXX/XXXX/XXXX",
"params": {},
"headers": {
"Content-type": "application/json"
},
"body": """{ "text": "{{ctx.payload}}"}"""
}
}
}
}
below is the structure of my logs in kibana
{
"_index": "index-beat",
"_type": "_doc",
"_id": "P3Toa34B1LVeuWotaVOY",
"_version": 1,
"_score": 1,
"_source": {
"#timestamp": "2022-01-18T06:38:19.559Z",
"name": "communication",
"loglevel": "ERROR",
"log": {
"file": {
"path": "/home/ubuntu/abc/abc/logs/communication.log"
},
"offset": 0
},
"timestamp": "2022-01-18T06:38:15.384279",
"exception": {
"ex_type": "None",
"ex": "None",
"tb": ""
},
"message": "{'err': 'Test'}"
},
"fields": {
"exception.ex_type": [
"None"
],
"loglevel.keyword": [
"ERROR"
],
"name.keyword": [
"communication"
],
"log.offset": [
0
],
"message": [
"{'err': 'Test'}"
],
"exception.tb": [
""
],
"exception.ex": [
"None"
],
"#timestamp": [
"2022-01-18T06:38:19.559Z"
],
"exception.tb.keyword": [
""
],
"loglevel": [
"ERROR"
],
"log.file.path": [
"/home/ubuntu/abc/abc/logs/communication.log"
],
"message.keyword": [
"{'err': 'Test'}"
],
"name": [
"communication"
],
"exception.ex_type.keyword": [
"None"
],
"exception.ex.keyword": [
"None"
],
"log.file.path.keyword": [
"/home/ubuntu/abc/abc/logs/communication.log"
],
"timestamp": [
"2022-01-18T06:38:15.384Z"
]
}
}
Please help me out in this one.

Can I get the user email within a Velocity template of AWS Amplify?

When I query a resolver in my GraphQL API, in which I have added a $util.error($ctx) to return the context object, I get the following result (removed unnecessary values).
{
"data": {
"listXData": null
},
"errors": [
{
"message": {
"arguments": {},
"args": {},
"info": {
"fieldName": "listXData",
"variables": {},
"parentTypeName": "Query",
"selectionSetList": [
"items",
"items/id",
"items/createdAt",
"items/updatedAt",
"nextToken"
],
"selectionSetGraphQL": "{\n items {\n id\n createdAt\n updatedAt\n }\n nextToken\n}"
},
"request": {...},
"identity": {
"sub": "",
"issuer": "",
"username": "013fe9d2-95f7-4885-83ec-b7e2e0a1423f",
"sourceIp": "",
"claims": {
"origin_jti": "",
"sub": "",
"event_id": "",
"token_use": "",
"scope": "",
"auth_time": ,
"iss": "",
"exp": ,
"iat": ,
"jti": "",
"client_id": "",
"username": "013fe9d2-95f7-4885-83ec-b7e2e0a1423f"
},
"defaultAuthStrategy": "ALLOW"
},
"stash": {},
"source": null,
"result": {
"items": [],
"scannedCount": 0,
"nextToken": null
},
"error": null,
"prev": {
"result": {}
}
},
"errorType": null,
"data": null,
"errorInfo": null,
"path": [
"listXData"
],
"locations": [
{
"line": 2,
"column": 3,
"sourceName": "GraphQL request"
}
]
}
]
}
As you can see, the username is an ID, however I would prefer to (also) have the email. Is it possible to get the user email (within the Velocity template)?
Let me know if I need to add more details or if my question is unclear.
The identity context only returns back the Cognito username for the user pool. You will need to setup pipeline functions to perform additional queries to get your user information. Here is one intro to setting them up.
At this point, it seems that it is not possible to do this purely by vtl.
I have implemented it using a lambda function, as follow:
Lambda function (node):
/* Amplify Params - DO NOT EDIT
ENV
REGION
Amplify Params - DO NOT EDIT */
const aws = require('aws-sdk')
const cognitoidentityserviceprovider = new aws.CognitoIdentityServiceProvider({
apiVersion: '2016-04-18',
region: 'eu-west-1'
})
exports.handler = async (context, event, callback) => {
if (!context.identity?.username) {
callback('Not signed in')
}
const params = {
'AccessToken': context.request.headers.authorization
}
const result = await cognitoidentityserviceprovider.getUser(params).promise()
const email = result.UserAttributes.find(attribute => attribute.Name === 'email')
callback(null, JSON.stringify({ email }))
}
CustomResources.json
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "An auto-generated nested stack.",
"Metadata": {...},
"Parameters": {...},
"Resources": {
"GetEmailLambdaDataSourceRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": {
"Fn::If": [
"HasEnvironmentParameter",
{
"Fn::Join": [
"-",
[
"GetEmail17ec",
{
"Ref": "GetAttGraphQLAPIApiId"
},
{
"Ref": "env"
}
]
]
},
{
"Fn::Join": [
"-",
[
"GetEmail17ec",
{
"Ref": "GetAttGraphQLAPIApiId"
}
]
]
}
]
},
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "appsync.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Policies": [
{
"PolicyName": "InvokeLambdaFunction",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": {
"Fn::If": [
"HasEnvironmentParameter",
{
"Fn::Sub": [
"arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:GetEmail-${env}",
{
"env": {
"Ref": "env"
}
}
]
},
{
"Fn::Sub": [
"arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:GetEmail",
{}
]
}
]
}
}
]
}
}
]
}
},
"GetEmailLambdaDataSource": {
"Type": "AWS::AppSync::DataSource",
"Properties": {
"ApiId": {
"Ref": "AppSyncApiId"
},
"Name": "GetEmailLambdaDataSource",
"Type": "AWS_LAMBDA",
"ServiceRoleArn": {
"Fn::GetAtt": [
"GetEmailLambdaDataSourceRole",
"Arn"
]
},
"LambdaConfig": {
"LambdaFunctionArn": {
"Fn::If": [
"HasEnvironmentParameter",
{
"Fn::Sub": [
"arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:GetEmail-${env}",
{
"env": {
"Ref": "env"
}
}
]
},
{
"Fn::Sub": [
"arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:GetEmail",
{}
]
}
]
}
}
},
"DependsOn": "GetEmailLambdaDataSourceRole"
},
"InvokeGetEmailLambdaDataSource": {
"Type": "AWS::AppSync::FunctionConfiguration",
"Properties": {
"ApiId": {
"Ref": "AppSyncApiId"
},
"Name": "InvokeGetEmailLambdaDataSource",
"DataSourceName": "GetEmailLambdaDataSource",
"FunctionVersion": "2018-05-29",
"RequestMappingTemplateS3Location": {
"Fn::Sub": [
"s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/pipelineFunctions/${ResolverFileName}",
{
"S3DeploymentBucket": {
"Ref": "S3DeploymentBucket"
},
"S3DeploymentRootKey": {
"Ref": "S3DeploymentRootKey"
},
"ResolverFileName": {
"Fn::Join": [
".",
[
"InvokeGetEmailLambdaDataSource",
"req",
"vtl"
]
]
}
}
]
},
"ResponseMappingTemplateS3Location": {
"Fn::Sub": [
"s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/pipelineFunctions/${ResolverFileName}",
{
"S3DeploymentBucket": {
"Ref": "S3DeploymentBucket"
},
"S3DeploymentRootKey": {
"Ref": "S3DeploymentRootKey"
},
"ResolverFileName": {
"Fn::Join": [
".",
[
"InvokeGetEmailLambdaDataSource",
"res",
"vtl"
]
]
}
}
]
}
},
"DependsOn": "GetEmailLambdaDataSource"
},
"IsOrganizationMember": {
"Type": "AWS::AppSync::FunctionConfiguration",
"Properties": {
"FunctionVersion": "2018-05-29",
"ApiId": {
"Ref": "AppSyncApiId"
},
"Name": "IsOrganizationMember",
"DataSourceName": "PermissionsPerOrganizationTable",
"RequestMappingTemplateS3Location": {
"Fn::Sub": [
"s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.isOrganizationMember.req.vtl",
{
"S3DeploymentBucket": {
"Ref": "S3DeploymentBucket"
},
"S3DeploymentRootKey": {
"Ref": "S3DeploymentRootKey"
}
}
]
},
"ResponseMappingTemplateS3Location": {
"Fn::Sub": [
"s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.isOrganizationMember.res.vtl",
{
"S3DeploymentBucket": {
"Ref": "S3DeploymentBucket"
},
"S3DeploymentRootKey": {
"Ref": "S3DeploymentRootKey"
}
}
]
}
}
},
"OrganizationAccessPipeline": {
"Type": "AWS::AppSync::Resolver",
"Properties": {
"ApiId": {
"Ref": "AppSyncApiId"
},
"TypeName": "Query",
"Kind": "PIPELINE",
"FieldName": "listXData",
"PipelineConfig": {
"Functions": [
{
"Fn::GetAtt": [
"InvokeGetEmailLambdaDataSource",
"FunctionId"
]
},
{
"Fn::GetAtt": [
"IsOrganizationMember",
"FunctionId"
]
}
]
},
"RequestMappingTemplate": "{}",
"ResponseMappingTemplate": "$util.toJson($ctx.result)"
}
}
},
"Conditions": {...},
"Outputs": {...}
}
The lambda is created with the CLI and IsOrganizationMember is a regular VTL which has the user email in the $context.prev.result.

Force angular cli gz generation

I'm trying to get a dist folder populated with the standard files + the .gz version of them. I did ng eject to get the webpack.config.js file so I could add the compression plugin https://github.com/webpack-contrib/compression-webpack-plugin. I added the new CompressPlugin({}) as the last plugin and marked the ejected: false in the .angular-cli.json file.
When I then run ng build I don't get any .gzip/.gz files generated which I would expect have.
Is there something I am missing or doing wrong? The full webpack file (mostly generated by ng new is:
const fs = require('fs');
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const autoprefixer = require('autoprefixer');
const postcssUrl = require('postcss-url');
const cssnano = require('cssnano');
const CompressionPlugin = require("compression-webpack-plugin");
const { NoEmitOnErrorsPlugin, SourceMapDevToolPlugin, NamedModulesPlugin } = require('webpack');
const { NamedLazyChunksWebpackPlugin, BaseHrefWebpackPlugin } = require('#angular/cli/plugins/webpack');
const { CommonsChunkPlugin } = require('webpack').optimize;
const { AotPlugin } = require('#ngtools/webpack');
const nodeModules = path.join(process.cwd(), 'node_modules');
const realNodeModules = fs.realpathSync(nodeModules);
const genDirNodeModules = path.join(process.cwd(), 'src', '$$_gendir', 'node_modules');
const entryPoints = ["inline","polyfills","sw-register","styles","vendor","main"];
const minimizeCss = false;
const baseHref = "";
const deployUrl = "";
const postcssPlugins = function () {
// safe settings based on: https://github.com/ben-eb/cssnano/issues/358#issuecomment-283696193
const importantCommentRe = /#preserve|#license|[##]\s*source(?:Mapping)?URL|^!/i;
const minimizeOptions = {
autoprefixer: false,
safe: true,
mergeLonghand: false,
discardComments: { remove: (comment) => !importantCommentRe.test(comment) }
};
return [
postcssUrl({
url: (URL) => {
// Only convert root relative URLs, which CSS-Loader won't process into require().
if (!URL.startsWith('/') || URL.startsWith('//')) {
return URL;
}
if (deployUrl.match(/:\/\//)) {
// If deployUrl contains a scheme, ignore baseHref use deployUrl as is.
return `${deployUrl.replace(/\/$/, '')}${URL}`;
}
else if (baseHref.match(/:\/\//)) {
// If baseHref contains a scheme, include it as is.
return baseHref.replace(/\/$/, '') +
`/${deployUrl}/${URL}`.replace(/\/\/+/g, '/');
}
else {
// Join together base-href, deploy-url and the original URL.
// Also dedupe multiple slashes into single ones.
return `/${baseHref}/${deployUrl}/${URL}`.replace(/\/\/+/g, '/');
}
}
}),
autoprefixer(),
].concat(minimizeCss ? [cssnano(minimizeOptions)] : []);
};
module.exports = {
"resolve": {
"extensions": [
".ts",
".js"
],
"modules": [
"./node_modules",
"./node_modules"
],
"symlinks": true
},
"resolveLoader": {
"modules": [
"./node_modules",
"./node_modules"
]
},
"entry": {
"main": [
"./src\\main.ts"
],
"polyfills": [
"./src\\polyfills.ts"
],
"styles": [
"./src\\styles.css"
]
},
"output": {
"path": path.join(process.cwd(), "dist"),
"filename": "[name].bundle.js",
"chunkFilename": "[id].chunk.js"
},
"module": {
"rules": [
{
"enforce": "pre",
"test": /\.js$/,
"loader": "source-map-loader",
"exclude": [
/(\\|\/)node_modules(\\|\/)/
]
},
{
"test": /\.html$/,
"loader": "raw-loader"
},
{
"test": /\.(eot|svg|cur)$/,
"loader": "file-loader?name=[name].[hash:20].[ext]"
},
{
"test": /\.(jpg|png|webp|gif|otf|ttf|woff|woff2|ani)$/,
"loader": "url-loader?name=[name].[hash:20].[ext]&limit=10000"
},
{
"exclude": [
path.join(process.cwd(), "src\\styles.css")
],
"test": /\.css$/,
"use": [
"exports-loader?module.exports.toString()",
{
"loader": "css-loader",
"options": {
"sourceMap": false,
"importLoaders": 1
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"plugins": postcssPlugins
}
}
]
},
{
"exclude": [
path.join(process.cwd(), "src\\styles.css")
],
"test": /\.scss$|\.sass$/,
"use": [
"exports-loader?module.exports.toString()",
{
"loader": "css-loader",
"options": {
"sourceMap": false,
"importLoaders": 1
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"plugins": postcssPlugins
}
},
{
"loader": "sass-loader",
"options": {
"sourceMap": false,
"precision": 8,
"includePaths": []
}
}
]
},
{
"exclude": [
path.join(process.cwd(), "src\\styles.css")
],
"test": /\.less$/,
"use": [
"exports-loader?module.exports.toString()",
{
"loader": "css-loader",
"options": {
"sourceMap": false,
"importLoaders": 1
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"plugins": postcssPlugins
}
},
{
"loader": "less-loader",
"options": {
"sourceMap": false
}
}
]
},
{
"exclude": [
path.join(process.cwd(), "src\\styles.css")
],
"test": /\.styl$/,
"use": [
"exports-loader?module.exports.toString()",
{
"loader": "css-loader",
"options": {
"sourceMap": false,
"importLoaders": 1
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"plugins": postcssPlugins
}
},
{
"loader": "stylus-loader",
"options": {
"sourceMap": false,
"paths": []
}
}
]
},
{
"include": [
path.join(process.cwd(), "src\\styles.css")
],
"test": /\.css$/,
"use": [
"style-loader",
{
"loader": "css-loader",
"options": {
"sourceMap": false,
"importLoaders": 1
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"plugins": postcssPlugins
}
}
]
},
{
"include": [
path.join(process.cwd(), "src\\styles.css")
],
"test": /\.scss$|\.sass$/,
"use": [
"style-loader",
{
"loader": "css-loader",
"options": {
"sourceMap": false,
"importLoaders": 1
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"plugins": postcssPlugins
}
},
{
"loader": "sass-loader",
"options": {
"sourceMap": false,
"precision": 8,
"includePaths": []
}
}
]
},
{
"include": [
path.join(process.cwd(), "src\\styles.css")
],
"test": /\.less$/,
"use": [
"style-loader",
{
"loader": "css-loader",
"options": {
"sourceMap": false,
"importLoaders": 1
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"plugins": postcssPlugins
}
},
{
"loader": "less-loader",
"options": {
"sourceMap": false
}
}
]
},
{
"include": [
path.join(process.cwd(), "src\\styles.css")
],
"test": /\.styl$/,
"use": [
"style-loader",
{
"loader": "css-loader",
"options": {
"sourceMap": false,
"importLoaders": 1
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"plugins": postcssPlugins
}
},
{
"loader": "stylus-loader",
"options": {
"sourceMap": false,
"paths": []
}
}
]
},
{
"test": /\.ts$/,
"loader": "#ngtools/webpack"
}
]
},
"plugins": [
new NoEmitOnErrorsPlugin(),
new CopyWebpackPlugin([
{
"context": "src",
"to": "",
"from": {
"glob": "assets/**/*",
"dot": true
}
},
{
"context": "src",
"to": "",
"from": {
"glob": "favicon.ico",
"dot": true
}
}
], {
"ignore": [
".gitkeep"
],
"debug": "warning"
}),
new ProgressPlugin(),
new CircularDependencyPlugin({
"exclude": /(\\|\/)node_modules(\\|\/)/,
"failOnError": false
}),
new NamedLazyChunksWebpackPlugin(),
new HtmlWebpackPlugin({
"template": "./src\\index.html",
"filename": "./index.html",
"hash": false,
"inject": true,
"compile": true,
"favicon": false,
"minify": false,
"cache": true,
"showErrors": true,
"chunks": "all",
"excludeChunks": [],
"title": "Webpack App",
"xhtml": true,
"chunksSortMode": function sort(left, right) {
let leftIndex = entryPoints.indexOf(left.names[0]);
let rightindex = entryPoints.indexOf(right.names[0]);
if (leftIndex > rightindex) {
return 1;
}
else if (leftIndex < rightindex) {
return -1;
}
else {
return 0;
}
}
}),
new BaseHrefWebpackPlugin({}),
new CommonsChunkPlugin({
"name": [
"inline"
],
"minChunks": null
}),
new CommonsChunkPlugin({
"name": [
"vendor"
],
"minChunks": (module) => {
return module.resource
&& (module.resource.startsWith(nodeModules)
|| module.resource.startsWith(genDirNodeModules)
|| module.resource.startsWith(realNodeModules));
},
"chunks": [
"main"
]
}),
new SourceMapDevToolPlugin({
"filename": "[file].map[query]",
"moduleFilenameTemplate": "[resource-path]",
"fallbackModuleFilenameTemplate": "[resource-path]?[hash]",
"sourceRoot": "webpack:///"
}),
new CommonsChunkPlugin({
"name": [
"main"
],
"minChunks": 2,
"async": "common"
}),
new NamedModulesPlugin({}),
new AotPlugin({
"mainPath": "main.ts",
"replaceExport": false,
"hostReplacementPaths": {
"environments\\environment.ts": "environments\\environment.ts"
},
"exclude": [],
"tsConfigPath": "src\\tsconfig.app.json",
"skipCodeGeneration": true
}),
new CompressionPlugin({})
],
"node": {
"fs": "empty",
"global": true,
"crypto": "empty",
"tls": "empty",
"net": "empty",
"process": true,
"module": false,
"clearImmediate": false,
"setImmediate": false
},
"devServer": {
"historyApiFallback": true
}
};
I'm using #angular/cli 1.4.4.
Note the main reason why I'm doing that is to improve some SEO recommendations because apparently my vendor.css is 600Kb big and being pointed out in the PageSpeed Ingishts: Your page has 1 blocking CSS resources. This causes a delay in rendering your page. It contains 3rd party css only - actually only bootstrap + font-awesome. Unless there is something else to prevent this I thought of compression. I also thought that the web server would auto compress and serve .gz files but I don't know whether that test would apply.
Any help is much appreciated! Thanks
Only ejected application's build is processed by exposed on the root webpack.config.js file. Application which is not ejected (or set to ejected: false) will completely ignore this webpack.config.js file.
So in order to use gzipped files you will need to work with ejected app and use new npm scripts automatically added to package.json.
Instead of 'ng build', now you should run:
npm run build
my related answers that may help:
Angular4 build - gzip missing
How to undo Angular 2 Cli ng-eject

Easiest way to import a simple csv file to a graph with OrientDB ETL

I would like to import a very simple directed graph file in csv to OrientDB. Concretely, the file is the roadNet-PA dataset from the SNAP collection https://snap.stanford.edu/data/roadNet-PA.html. The first lines of the file are as follows:
# Directed graph (each unordered pair of nodes is saved once)
# Pennsylvania road network
# Nodes: 1088092 Edges: 3083796
# FromNodeId ToNodeId
0 1
0 6309
0 6353
1 0
6353 0
6353 6354
There is only one type of vertex (a road intersection) and edges have no information (I suppose OrientDB lightweight edges are the best option for this). Note also that vertices are spaced with tabs.
I've tried to create a simple etl to import the file with no success. Here is the etl:
{
"config": {
"log": "debug"
},
"source" : {
"file": { "path": "/tmp/roadNet-PA.csv" }
},
"extractor": { "row": {} },
"transformers": [
{ "csv": { "separator": " ", "skipFrom": 1, "skipTo": 4 } },
{ "vertex": { "class": "Intersection" } },
{ "edge": { "class": "Road" } }
],
"loader": {
"orientdb": {
"dbURL": "remote:localhost/roads",
"dbType": "graph",
"classes": [
{"name": "Intersection", "extends": "V"},
{"name": "Road", "extends": "E"}
], "indexes": [
{"class":"Intersection", "fields":["id:integer"], "type":"UNIQUE" }
]
}
}
}
The etl works but it does not import the file as I expect. I suppose the problem is in the transformers. My idea is to read the csv line by line and create and edge connecting both vertices, but I'm not sure how to express this in an etl file. Any ideas?
Try this:
{
"config": {
"log": "debug"
},
"source" : {
"file": { "path": "/tmp/roadNet-PA.csv" }
},
"extractor": { "row": {} },
"transformers": [
{ "csv": { "separator": "\t", "skipFrom": 1, "skipTo": 4,
"columnsOnFirstLine": false,
"columns":["id", "to"] } },
{ "vertex": { "class": "Intersection" } },
{ "merge": { "joinFieldName":"id", "lookup":"Intersection.id" } },
{ "edge": {
"class": "Road",
"joinFieldName": "to",
"lookup": "Intersection.id",
"unresolvedLinkAction": "CREATE"
}
},
],
"loader": {
"orientdb": {
"dbURL": "remote:localhost/roads",
"dbType": "graph",
"wal": false,
"batchCommit": 1000,
"tx": true,
"txUseLog": false,
"useLightweightEdges" : true,
"classes": [
{"name": "Intersection", "extends": "V"},
{"name": "Road", "extends": "E"}
], "indexes": [
{"class":"Intersection", "fields":["id:integer"], "type":"UNIQUE" }
]
}
}
}
To speedup loading I suggest you to shutdown the server, and import the ETL by using "plocal:" instead of "remote:". Example replacing the existent with:
"dbURL": "plocal:/orientdb/databases/roads",
It finally worked. I've moved the merge before vertex line as suggested by Luca. I've also changed the 'id' field to 'from' to avoid the error "property key is reserved for all elements id". Here is the snippet:
{
"config": {
"log": "debug"
},
"source" : {
"file": { "path": "/tmp/roads.csv" }
},
"extractor": { "row": {} },
"transformers": [
{ "csv": { "separator": "\t",
"columnsOnFirstLine": false,
"columns":["from", "to"] } },
{ "merge": { "joinFieldName":"from", "lookup":"Intersection.from" } },
{ "vertex": { "class": "Intersection" } },
{ "edge": {
"class": "Road",
"joinFieldName": "to",
"lookup": "Intersection.from",
"unresolvedLinkAction": "CREATE"
}
},
],
"loader": {
"orientdb": {
"dbURL": "remote:localhost/roads",
"dbType": "graph",
"wal": false,
"batchCommit": 1000,
"tx": true,
"txUseLog": false,
"useLightweightEdges" : true,
"classes": [
{"name": "Intersection", "extends": "V"},
{"name": "Road", "extends": "E"}
], "indexes": [
{"class":"Intersection", "fields":["from:integer"], "type":"UNIQUE" }
]
}
}
}

Resources