mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-17 20:50:44 +00:00
Initial checkin of API module
This commit is contained in:
@ -179,9 +179,7 @@
|
||||
<property name="ignoreFinal" value="false"/>
|
||||
<property name="allowedAbbreviationLength" value="1"/>
|
||||
</module>
|
||||
-->
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
<!--
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
<module name="CustomImportOrder">
|
||||
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||
|
3
pom.xml
3
pom.xml
@ -30,8 +30,9 @@
|
||||
|
||||
<modules>
|
||||
<module>qqq-backend-core</module>
|
||||
<module>qqq-backend-module-rdbms</module>
|
||||
<module>qqq-backend-module-api</module>
|
||||
<module>qqq-backend-module-filesystem</module>
|
||||
<module>qqq-backend-module-rdbms</module>
|
||||
<module>qqq-middleware-picocli</module>
|
||||
<module>qqq-middleware-javalin</module>
|
||||
<module>qqq-sample-project</module>
|
||||
|
@ -59,7 +59,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.13.2.1</version>
|
||||
<version>2.14.0-rc1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
|
@ -43,6 +43,7 @@ public class ProcessSummaryLine implements ProcessSummaryLineInterface
|
||||
private String pluralFutureMessage;
|
||||
private String singularPastMessage;
|
||||
private String pluralPastMessage;
|
||||
private String messageSuffix;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// using ArrayList, because we need to be Serializable, and List is not //
|
||||
@ -394,11 +395,13 @@ public class ProcessSummaryLine implements ProcessSummaryLineInterface
|
||||
{
|
||||
if(count.equals(1))
|
||||
{
|
||||
setMessage(isPast ? getSingularPastMessage() : getSingularFutureMessage());
|
||||
setMessage((isPast ? getSingularPastMessage() : getSingularFutureMessage())
|
||||
+ (messageSuffix == null ? "" : messageSuffix));
|
||||
}
|
||||
else
|
||||
{
|
||||
setMessage(isPast ? getPluralPastMessage() : getPluralFutureMessage());
|
||||
setMessage((isPast ? getPluralPastMessage() : getPluralFutureMessage())
|
||||
+ (messageSuffix == null ? "" : messageSuffix));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -417,4 +420,38 @@ public class ProcessSummaryLine implements ProcessSummaryLineInterface
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for messageSuffix
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getMessageSuffix()
|
||||
{
|
||||
return messageSuffix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for messageSuffix
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setMessageSuffix(String messageSuffix)
|
||||
{
|
||||
this.messageSuffix = messageSuffix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for messageSuffix
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessSummaryLine withMessageSuffix(String messageSuffix)
|
||||
{
|
||||
this.messageSuffix = messageSuffix;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.actions.processes;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||
@ -241,4 +242,18 @@ public class RunBackendStepOutput extends AbstractActionOutput implements Serial
|
||||
return (ValueUtils.getValueAsBigDecimal(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addRecord(QRecord record)
|
||||
{
|
||||
if(this.processState.getRecords() == null)
|
||||
{
|
||||
this.processState.setRecords(new ArrayList<>());
|
||||
}
|
||||
this.processState.getRecords().add(record);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.tables.insert;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
@ -54,4 +55,19 @@ public class InsertOutput extends AbstractActionOutput
|
||||
{
|
||||
this.records = records;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addRecord(QRecord record)
|
||||
{
|
||||
if(this.records == null)
|
||||
{
|
||||
this.records = new ArrayList<>();
|
||||
}
|
||||
this.records.add(record);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -75,7 +75,8 @@ public class QBackendModuleDispatcher
|
||||
"com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.filesystem.local.FilesystemBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.filesystem.s3.S3BackendModule"
|
||||
"com.kingsrook.qqq.backend.module.filesystem.s3.S3BackendModule",
|
||||
"com.kingsrook.qqq.backend.module.api.APIBackendModule"
|
||||
};
|
||||
|
||||
for(String moduleClassName : moduleClassNames)
|
||||
|
@ -71,4 +71,18 @@ public class StreamedBackendStepOutput extends RunBackendStepOutput
|
||||
return (outputRecords);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void addRecord(QRecord record)
|
||||
{
|
||||
if(this.outputRecords == null)
|
||||
{
|
||||
this.outputRecords = new ArrayList<>();
|
||||
}
|
||||
this.outputRecords.add(record);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.utils;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@ -29,6 +30,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -422,4 +424,57 @@ public class CollectionUtils
|
||||
}
|
||||
return (list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Returns the input collection, unless it was null - in which case a new array list is returned.
|
||||
**
|
||||
** Meant to help avoid null checks on foreach loops.
|
||||
*******************************************************************************/
|
||||
public static <T> Collection<T> nonNullCollection(Collection<T> list)
|
||||
{
|
||||
if(list == null)
|
||||
{
|
||||
return (new ArrayList<>());
|
||||
}
|
||||
return (list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Convert a collection of QRecords to a map, from one field's values out of
|
||||
** those records, to another field's value from those records
|
||||
*******************************************************************************/
|
||||
public static Map<Serializable, Serializable> recordsToMap(Collection<QRecord> records, String keyFieldName, String valueFieldName)
|
||||
{
|
||||
Map<Serializable, Serializable> rs = new HashMap<>();
|
||||
|
||||
for(QRecord record : nonNullCollection(records))
|
||||
{
|
||||
rs.put(record.getValue(keyFieldName), record.getValue(valueFieldName));
|
||||
}
|
||||
|
||||
return (rs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Convert a collection of QRecords to a map, from one field's values out of
|
||||
** those records, to the records themselves.
|
||||
*******************************************************************************/
|
||||
public static Map<Serializable, QRecord> recordsToMap(Collection<QRecord> records, String keyFieldName)
|
||||
{
|
||||
Map<Serializable, QRecord> rs = new HashMap<>();
|
||||
|
||||
for(QRecord record : nonNullCollection(records))
|
||||
{
|
||||
rs.put(record.getValue(keyFieldName), record);
|
||||
}
|
||||
|
||||
return (rs);
|
||||
}
|
||||
|
||||
}
|
||||
|
22
qqq-backend-module-api/README.md
Normal file
22
qqq-backend-module-api/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# qqq-backend-module-api
|
||||
This is a backend-module for the qqq framework - specifically, one that
|
||||
works with an API (as in, a web service API, over http(s)).
|
||||
|
||||
## License
|
||||
QQQ - Low-code Application Framework for Engineers. \
|
||||
Copyright (C) 2022. Kingsrook, LLC \
|
||||
651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States \
|
||||
contact@kingsrook.com | https://github.com/Kingsrook/
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
86
qqq-backend-module-api/pom.xml
Normal file
86
qqq-backend-module-api/pom.xml
Normal file
@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ QQQ - Low-code Application Framework for Engineers.
|
||||
~ Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
~ 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
~ contact@kingsrook.com
|
||||
~ https://github.com/Kingsrook/
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero General Public License as
|
||||
~ published by the Free Software Foundation, either version 3 of the
|
||||
~ License, or (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>qqq-backend-module-api</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-parent-project</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<!-- props specifically to this module -->
|
||||
<!-- none at this time -->
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- other qqq modules deps -->
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Common deps for all qqq modules -->
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>com/kingsrook/qqq/backend/module/api/model/**/*.class</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.UpdateInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableBackendDetails;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
import com.kingsrook.qqq.backend.module.api.actions.APIInsertAction;
|
||||
// import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSCountAction;
|
||||
// import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSDeleteAction;
|
||||
// import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSInsertAction;
|
||||
// import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSQueryAction;
|
||||
// import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSUpdateAction;
|
||||
// import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
// import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QQQ Backend module for working with API's (e.g., over http(s)).
|
||||
*******************************************************************************/
|
||||
public class APIBackendModule implements QBackendModuleInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Method where a backend module must be able to provide its type (name).
|
||||
*******************************************************************************/
|
||||
public String getBackendType()
|
||||
{
|
||||
return ("api");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for backend meta data for this module.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Class<? extends QBackendMetaData> getBackendMetaDataClass()
|
||||
{
|
||||
return (null); //return (RDBMSBackendMetaData.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for table-backend details for this module.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Class<? extends QTableBackendDetails> getTableBackendDetailsClass()
|
||||
{
|
||||
return (null); //return (RDBMSTableBackendDetails.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public CountInterface getCountInterface()
|
||||
{
|
||||
return (null); //return (new RDBMSCountAction());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QueryInterface getQueryInterface()
|
||||
{
|
||||
return (null); //return (new RDBMSQueryAction());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public InsertInterface getInsertInterface()
|
||||
{
|
||||
return (new APIInsertAction());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public UpdateInterface getUpdateInterface()
|
||||
{
|
||||
return (null); //return (new RDBMSUpdateAction());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public DeleteInterface getDeleteInterface()
|
||||
{
|
||||
return (null); //return (new RDBMSDeleteAction());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api.actions;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class APIInsertAction extends AbstractAPIAction implements InsertInterface
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(APIInsertAction.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public InsertOutput execute(InsertInput insertInput) throws QException
|
||||
{
|
||||
InsertOutput insertOutput = new InsertOutput();
|
||||
insertOutput.setRecords(new ArrayList<>());
|
||||
|
||||
if(CollectionUtils.nullSafeIsEmpty(insertInput.getRecords()))
|
||||
{
|
||||
LOG.info("Insert request called with 0 records. Returning with no-op");
|
||||
return (insertOutput);
|
||||
}
|
||||
|
||||
QTableMetaData table = insertInput.getTable();
|
||||
|
||||
preAction(insertInput);
|
||||
|
||||
try
|
||||
{
|
||||
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
|
||||
HttpClient client = httpClientBuilder.build();
|
||||
|
||||
String url = apiActionUtil.buildTableUrl(table);
|
||||
HttpPost request = new HttpPost(url);
|
||||
apiActionUtil.setupAuthorizationInRequest(request);
|
||||
apiActionUtil.setupContentTypeInRequest(request);
|
||||
apiActionUtil.setupAdditionalHeaders(request);
|
||||
|
||||
// todo - supports bulk post?
|
||||
|
||||
for(QRecord record : insertInput.getRecords())
|
||||
{
|
||||
try
|
||||
{
|
||||
request.setEntity(apiActionUtil.recordToEntity(table, record));
|
||||
|
||||
HttpResponse response = client.execute(request);
|
||||
|
||||
QRecord outputRecord = apiActionUtil.processPostResponse(table, record, response);
|
||||
insertOutput.addRecord(outputRecord);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
record.addError("Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return (insertOutput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error in API Insert", e);
|
||||
throw new QException("Error executing insert: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api.actions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.api.model.metadata.APIBackendMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base class for all Backend-module-API Actions
|
||||
*******************************************************************************/
|
||||
public abstract class AbstractAPIAction
|
||||
{
|
||||
protected APIBackendMetaData backendMetaData;
|
||||
protected BaseAPIActionUtil apiActionUtil;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setup the s3 utils object to be used for this action.
|
||||
*******************************************************************************/
|
||||
public void preAction(AbstractTableActionInput actionInput)
|
||||
{
|
||||
QBackendMetaData baseBackendMetaData = actionInput.getInstance().getBackendForTable(actionInput.getTableName());
|
||||
this.backendMetaData = (APIBackendMetaData) baseBackendMetaData;
|
||||
|
||||
if(backendMetaData.getActionUtil() != null)
|
||||
{
|
||||
apiActionUtil = QCodeLoader.getAdHoc(BaseAPIActionUtil.class, backendMetaData.getActionUtil());
|
||||
}
|
||||
else
|
||||
{
|
||||
apiActionUtil = new BaseAPIActionUtil();
|
||||
}
|
||||
|
||||
apiActionUtil.setBackendMetaData(this.backendMetaData);
|
||||
apiActionUtil.setActionInput(actionInput);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api.actions;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.module.api.model.metadata.APIBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.api.model.metadata.APITableBackendDetails;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.AbstractHttpEntity;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base class for utility functions that make up the unique ways in which an
|
||||
** API can be implemented.
|
||||
*******************************************************************************/
|
||||
public class BaseAPIActionUtil
|
||||
{
|
||||
protected APIBackendMetaData backendMetaData;
|
||||
protected AbstractTableActionInput actionInput;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** As part of making a request - set up its authorization header (not just
|
||||
** strictly "Authorization", but whatever is needed for auth).
|
||||
**
|
||||
** Can be overridden if an API uses an authorization type we don't natively support.
|
||||
*******************************************************************************/
|
||||
protected void setupAuthorizationInRequest(HttpRequestBase request)
|
||||
{
|
||||
switch(backendMetaData.getAuthorizationType())
|
||||
{
|
||||
case BASIC_AUTH_API_KEY:
|
||||
request.addHeader("Authorization", getBasicAuthenticationHeader(backendMetaData.getApiKey()));
|
||||
break;
|
||||
|
||||
case BASIC_AUTH_USERNAME_PASSWORD:
|
||||
request.addHeader("Authorization", getBasicAuthenticationHeader(backendMetaData.getUsername(), backendMetaData.getPassword()));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected authorization type: " + backendMetaData.getAuthorizationType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** As part of making a request - set up its content-type header.
|
||||
*******************************************************************************/
|
||||
protected void setupContentTypeInRequest(HttpRequestBase request)
|
||||
{
|
||||
request.addHeader("Content-Type", backendMetaData.getContentType());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Helper method to create a value for an Authentication header, using just an
|
||||
** apiKey - encoded as Basic + base64(apiKey)
|
||||
*******************************************************************************/
|
||||
protected String getBasicAuthenticationHeader(String apiKey)
|
||||
{
|
||||
return "Basic " + Base64.getEncoder().encodeToString(apiKey.getBytes());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** As part of making a request - set up additional headers. Noop in base -
|
||||
** meant to override in subclasses.
|
||||
*******************************************************************************/
|
||||
public void setupAdditionalHeaders(HttpRequestBase request)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Helper method to create a value for an Authentication header, using just a
|
||||
** username & password - encoded as Basic + base64(username:password)
|
||||
*******************************************************************************/
|
||||
protected String getBasicAuthenticationHeader(String username, String password)
|
||||
{
|
||||
return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Helper method to build the URL for a table. Can be overridden, if a
|
||||
** particular API isn't just "base" + "tablePath".
|
||||
**
|
||||
** Note: you may want to look at the actionInput object, to help figure out
|
||||
** what path you need, depending on your API.
|
||||
*******************************************************************************/
|
||||
protected String buildTableUrl(QTableMetaData table)
|
||||
{
|
||||
return (backendMetaData.getBaseUrl() + getBackendDetails(table).getTablePath());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Build an HTTP Entity (e.g., for a PUT or POST) from a QRecord. Can be
|
||||
** overridden if an API doesn't do a basic json object. Or, can override a
|
||||
** helper method, such as recordToJsonObject.
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected AbstractHttpEntity recordToEntity(QTableMetaData table, QRecord record) throws IOException
|
||||
{
|
||||
JSONObject body = recordToJsonObject(table, record);
|
||||
String json = body.toString(3);
|
||||
System.out.println(json);
|
||||
return (new StringEntity(json));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Helper for recordToEntity - builds a basic JSON object. Can be
|
||||
** overridden if an API doesn't do a basic json object.
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected JSONObject recordToJsonObject(QTableMetaData table, QRecord record)
|
||||
{
|
||||
JSONObject body = new JSONObject();
|
||||
for(Map.Entry<String, Serializable> entry : record.getValues().entrySet())
|
||||
{
|
||||
String fieldName = entry.getKey();
|
||||
Serializable value = entry.getValue();
|
||||
|
||||
QFieldMetaData field;
|
||||
try
|
||||
{
|
||||
field = table.getField(fieldName);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
////////////////////////////////////
|
||||
// skip values that aren't fields //
|
||||
////////////////////////////////////
|
||||
continue;
|
||||
}
|
||||
body.put(getFieldBackendName(field), value);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected QRecord processPostResponse(QTableMetaData table, QRecord record, HttpResponse response) throws IOException
|
||||
{
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
System.out.println(statusCode);
|
||||
|
||||
HttpEntity entity = response.getEntity();
|
||||
String resultString = EntityUtils.toString(entity);
|
||||
System.out.println(resultString);
|
||||
|
||||
JSONObject jsonObject = JsonUtils.toJSONObject(resultString);
|
||||
|
||||
String primaryKeyFieldName = table.getPrimaryKeyField();
|
||||
String primaryKeyBackendName = getFieldBackendName(table.getField(primaryKeyFieldName));
|
||||
if(jsonObject.has(primaryKeyBackendName))
|
||||
{
|
||||
Serializable primaryKey = (Serializable) jsonObject.get(primaryKeyBackendName);
|
||||
record.setValue(primaryKeyFieldName, primaryKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(jsonObject.has("error"))
|
||||
{
|
||||
JSONObject errorObject = jsonObject.getJSONObject("error");
|
||||
if(errorObject.has("message"))
|
||||
{
|
||||
record.addError("Error: " + errorObject.getString("message"));
|
||||
}
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeIsEmpty(record.getErrors()))
|
||||
{
|
||||
record.addError("Unspecified error executing insert.");
|
||||
}
|
||||
}
|
||||
|
||||
return (record);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected APITableBackendDetails getBackendDetails(QTableMetaData tableMetaData)
|
||||
{
|
||||
return (APITableBackendDetails) tableMetaData.getBackendDetails();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected String getFieldBackendName(QFieldMetaData field)
|
||||
{
|
||||
String backendName = field.getBackendName();
|
||||
if(!StringUtils.hasContent(backendName))
|
||||
{
|
||||
backendName = field.getName();
|
||||
}
|
||||
return (backendName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendMetaData
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APIBackendMetaData getBackendMetaData()
|
||||
{
|
||||
return backendMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendMetaData
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBackendMetaData(APIBackendMetaData backendMetaData)
|
||||
{
|
||||
this.backendMetaData = backendMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for actionInput
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AbstractTableActionInput getActionInput()
|
||||
{
|
||||
return actionInput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for actionInput
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setActionInput(AbstractTableActionInput actionInput)
|
||||
{
|
||||
this.actionInput = actionInput;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api.model;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum AuthorizationType
|
||||
{
|
||||
BASIC_AUTH_API_KEY,
|
||||
BASIC_AUTH_USERNAME_PASSWORD
|
||||
|
||||
}
|
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api.model.metadata;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.module.api.APIBackendModule;
|
||||
import com.kingsrook.qqq.backend.module.api.model.AuthorizationType;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Meta-data to provide details of an API backend (e.g., connection params)
|
||||
*******************************************************************************/
|
||||
public class APIBackendMetaData extends QBackendMetaData
|
||||
{
|
||||
private String baseUrl;
|
||||
private String apiKey;
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
private AuthorizationType authorizationType;
|
||||
private String contentType; // todo enum?
|
||||
|
||||
private QCodeReference actionUtil;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default Constructor.
|
||||
*******************************************************************************/
|
||||
public APIBackendMetaData()
|
||||
{
|
||||
super();
|
||||
setBackendType(APIBackendModule.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter, override to help fluent flows
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public APIBackendMetaData withName(String name)
|
||||
{
|
||||
setName(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
// todo?
|
||||
// /*******************************************************************************
|
||||
// ** Called by the QInstanceEnricher - to do backend-type-specific enrichments.
|
||||
// ** Original use case is: reading secrets into fields (e.g., passwords).
|
||||
// *******************************************************************************/
|
||||
// @Override
|
||||
// public void enrich()
|
||||
// {
|
||||
// super.enrich();
|
||||
// QMetaDataVariableInterpreter interpreter = new QMetaDataVariableInterpreter();
|
||||
// username = interpreter.interpret(username);
|
||||
// password = interpreter.interpret(password);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for baseUrl
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getBaseUrl()
|
||||
{
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for baseUrl
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBaseUrl(String baseUrl)
|
||||
{
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for baseUrl
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APIBackendMetaData withBaseUrl(String baseUrl)
|
||||
{
|
||||
this.baseUrl = baseUrl;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for apiKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getApiKey()
|
||||
{
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for apiKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setApiKey(String apiKey)
|
||||
{
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for apiKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APIBackendMetaData withApiKey(String apiKey)
|
||||
{
|
||||
this.apiKey = apiKey;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for username
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getUsername()
|
||||
{
|
||||
return username;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for username
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setUsername(String username)
|
||||
{
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for username
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APIBackendMetaData withUsername(String username)
|
||||
{
|
||||
this.username = username;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for password
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for password
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for password
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APIBackendMetaData withPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for authorizationType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AuthorizationType getAuthorizationType()
|
||||
{
|
||||
return authorizationType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for authorizationType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setAuthorizationType(AuthorizationType authorizationType)
|
||||
{
|
||||
this.authorizationType = authorizationType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for authorizationType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APIBackendMetaData withAuthorizationType(AuthorizationType authorizationType)
|
||||
{
|
||||
this.authorizationType = authorizationType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for contentType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getContentType()
|
||||
{
|
||||
return contentType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for contentType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setContentType(String contentType)
|
||||
{
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for contentType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APIBackendMetaData withContentType(String contentType)
|
||||
{
|
||||
this.contentType = contentType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for actionUtil
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QCodeReference getActionUtil()
|
||||
{
|
||||
return actionUtil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for actionUtil
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setActionUtil(QCodeReference actionUtil)
|
||||
{
|
||||
this.actionUtil = actionUtil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for actionUtil
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APIBackendMetaData withActionUtil(QCodeReference actionUtil)
|
||||
{
|
||||
this.actionUtil = actionUtil;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api.model.metadata;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableBackendDetails;
|
||||
import com.kingsrook.qqq.backend.module.api.APIBackendModule;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Extension of QTableBackendDetails, with details specific to an API table.
|
||||
*******************************************************************************/
|
||||
public class APITableBackendDetails extends QTableBackendDetails
|
||||
{
|
||||
private String tablePath;
|
||||
private String tableWrapperObjectName;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default Constructor.
|
||||
*******************************************************************************/
|
||||
public APITableBackendDetails()
|
||||
{
|
||||
super();
|
||||
setBackendType(APIBackendModule.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tablePath
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getTablePath()
|
||||
{
|
||||
return tablePath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tablePath
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTablePath(String tablePath)
|
||||
{
|
||||
this.tablePath = tablePath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for tablePath
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APITableBackendDetails withTablePath(String tablePath)
|
||||
{
|
||||
this.tablePath = tablePath;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableWrapperObjectName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getTableWrapperObjectName()
|
||||
{
|
||||
return tableWrapperObjectName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableWrapperObjectName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTableWrapperObjectName(String tableWrapperObjectName)
|
||||
{
|
||||
this.tableWrapperObjectName = tableWrapperObjectName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableWrapperObjectName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APITableBackendDetails withTableWrapperObjectName(String tableWrapperObjectName)
|
||||
{
|
||||
this.tableWrapperObjectName = tableWrapperObjectName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.module.api.model.metadata.APIBackendMetaData;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class EasyPostApiTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPostTrackerSuccess() throws QException
|
||||
{
|
||||
QRecord record = new QRecord()
|
||||
.withValue("__ignoreMe", "123")
|
||||
.withValue("carrierCode", "USPS")
|
||||
.withValue("trackingNo", "EZ1000000001");
|
||||
|
||||
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName("easypostTracker");
|
||||
insertInput.setRecords(List.of(record));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
QRecord outputRecord = insertOutput.getRecords().get(0);
|
||||
assertNotNull(outputRecord.getValue("id"), "Should get a tracker id");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPostTrackerEmptyInput() throws QException
|
||||
{
|
||||
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName("easypostTracker");
|
||||
insertInput.setRecords(List.of());
|
||||
new InsertAction().execute(insertInput);
|
||||
|
||||
////////////////////////////////////
|
||||
// just make sure we don't throw. //
|
||||
////////////////////////////////////
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPostTrackerBadApiKey() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
QBackendMetaData backend = instance.getBackend(TestUtils.EASYPOST_BACKEND_NAME);
|
||||
((APIBackendMetaData) backend).setApiKey("not-valid");
|
||||
|
||||
QRecord record = new QRecord()
|
||||
.withValue("carrierCode", "USPS")
|
||||
.withValue("trackingNo", "EZ1000000001");
|
||||
|
||||
InsertInput insertInput = new InsertInput(instance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName("easypostTracker");
|
||||
insertInput.setRecords(List.of(record));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
QRecord outputRecord = insertOutput.getRecords().get(0);
|
||||
assertNull(outputRecord.getValue("id"), "Should not get a tracker id");
|
||||
assertThat(outputRecord.getErrors()).isNotNull().isNotEmpty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPostTrackerError() throws QException
|
||||
{
|
||||
QRecord record = new QRecord()
|
||||
.withValue("carrierCode", "USPS")
|
||||
.withValue("trackingNo", "Not-Valid-Tracking-No");
|
||||
|
||||
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName("easypostTracker");
|
||||
insertInput.setRecords(List.of(record));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
QRecord outputRecord = insertOutput.getRecords().get(0);
|
||||
assertNull(outputRecord.getValue("id"), "Should not get a tracker id");
|
||||
assertThat(outputRecord.getErrors()).isNotNull().isNotEmpty();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright © 2022-2022. Nutrifresh Services <contact@nutrifreshservices.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.module.api.actions.BaseAPIActionUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility methods for working with EasyPost API
|
||||
*******************************************************************************/
|
||||
public class EasyPostUtils extends BaseAPIActionUtil
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(EasyPostUtils.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
protected JSONObject recordToJsonObject(QTableMetaData table, QRecord record)
|
||||
{
|
||||
JSONObject inner = super.recordToJsonObject(table, record);
|
||||
JSONObject outer = new JSONObject();
|
||||
outer.put(getBackendDetails(table).getTableWrapperObjectName(), inner);
|
||||
return (outer);
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.api;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData;
|
||||
import com.kingsrook.qqq.backend.module.api.model.AuthorizationType;
|
||||
import com.kingsrook.qqq.backend.module.api.model.metadata.APIBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.api.model.metadata.APITableBackendDetails;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class TestUtils
|
||||
{
|
||||
public static final String EASYPOST_BACKEND_NAME = "easypost";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QInstance defineInstance()
|
||||
{
|
||||
QInstance qInstance = new QInstance();
|
||||
qInstance.addBackend(defineBackend());
|
||||
qInstance.addTable(defineTableEasypostTracker());
|
||||
qInstance.setAuthentication(defineAuthentication());
|
||||
return (qInstance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define the authentication used in standard tests - using 'mock' type.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QAuthenticationMetaData defineAuthentication()
|
||||
{
|
||||
return new QAuthenticationMetaData()
|
||||
.withName("mock")
|
||||
.withType(QAuthenticationType.MOCK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QBackendMetaData defineBackend()
|
||||
{
|
||||
String apiKey = new QMetaDataVariableInterpreter().interpret("${env.EASYPOST_API_KEY}");
|
||||
|
||||
return (new APIBackendMetaData()
|
||||
.withName("easypost")
|
||||
.withApiKey(apiKey)
|
||||
.withAuthorizationType(AuthorizationType.BASIC_AUTH_API_KEY)
|
||||
.withBaseUrl("https://api.easypost.com/v2/")
|
||||
.withContentType("application/json")
|
||||
.withActionUtil(new QCodeReference(EasyPostUtils.class, QCodeUsage.CUSTOMIZER))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QTableMetaData defineTableEasypostTracker()
|
||||
{
|
||||
return (new QTableMetaData()
|
||||
.withName("easypostTracker")
|
||||
.withBackendName("easypost")
|
||||
.withField(new QFieldMetaData("id", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("trackingNo", QFieldType.STRING).withBackendName("tracking_code"))
|
||||
.withField(new QFieldMetaData("carrierCode", QFieldType.STRING).withBackendName("carrier"))
|
||||
.withPrimaryKeyField("id")
|
||||
.withBackendDetails(new APITableBackendDetails()
|
||||
.withTablePath("trackers")
|
||||
.withTableWrapperObjectName("tracker")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -48,12 +48,12 @@
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.28</version>
|
||||
<version>8.0.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.1.210</version>
|
||||
<version>2.1.214</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -65,6 +65,7 @@ NF_ONE_REPO_NAME="Nutrifresh-One"
|
||||
QQQ_SAMPLE_PROJECT_MODULE_NAME="qqq-sample-project"
|
||||
QQQ_BACKEND_CORE_MODULE_NAME="qqq-backend-core"
|
||||
QQQ_BACKEND_MODULE_RDBMS_MODULE_NAME="qqq-backend-module-rdbms"
|
||||
QQQ_BACKEND_MODULE_API_MODULE_NAME="qqq-backend-module-api"
|
||||
QQQ_DEV_TOOLS_MODULE_NAME="qqq-dev-tools"
|
||||
|
||||
|
||||
@ -82,6 +83,7 @@ QQQ_REPO_LIST=(
|
||||
QQQ_MODULE_LIST=(
|
||||
$QQQ_SAMPLE_PROJECT_MODULE_NAME
|
||||
$QQQ_BACKEND_MODULE_RDBMS_MODULE_NAME
|
||||
$QQQ_BACKEND_MODULE_API_MODULE_NAME
|
||||
$QQQ_BACKEND_CORE_MODULE_NAME
|
||||
$QQQ_DEV_TOOLS_MODULE_NAME
|
||||
)
|
||||
|
Reference in New Issue
Block a user